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.
- package/.eslintrc.js +1 -0
- package/.github/workflows/tests.yml +7 -5
- package/.nvmrc +1 -0
- package/api.yml +150 -0
- package/auth/authHandler.js +2 -2
- package/cli.js +36 -0
- package/closeAll.js +6 -0
- package/controllers/__tests__/admin.js +12 -9
- package/controllers/__tests__/app.js +57 -5
- package/controllers/__tests__/bookings.js +20 -10
- package/controllers/__tests__/ping.js +8 -3
- package/controllers/__tests__/user.js +9 -3
- package/controllers/admin.js +46 -1
- package/controllers/app.js +174 -1
- package/controllers/ping.js +2 -1
- package/controllers/user.js +2 -6
- package/index.js +44 -8
- package/jest.setup.js +11 -0
- package/jsdoc.conf.json +1 -1
- package/migrations/20200128162519-deletedAtForIntegrations.js +0 -1
- package/migrations/20210408024232-expand-JWT-field-size.js +2 -2
- package/migrations/20220426153807-cronJobs.js +41 -0
- package/models/cronjobs.js +31 -0
- package/models/db.js +0 -1
- package/models/index.js +1 -0
- package/package.json +14 -3
- package/test/plugin.js +40 -13
- package/test/utils.js +3 -5
- package/test/worker.js +5 -0
- package/tutorials/plugin-development.md +6 -3
- package/tutorials/setup-your-instance.md +70 -18
- package/worker/index.js +53 -0
- package/worker/queue.js +88 -0
package/controllers/app.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
const jwt = require('jwt-promise');
|
|
2
2
|
const { omit } = require('ramda');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const { Umzug, SequelizeStorage } = require('umzug');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const Sequelize = require('sequelize');
|
|
7
|
+
const fs = require('fs').promises;
|
|
8
|
+
const bb = require('bluebird');
|
|
9
|
+
const R = require('ramda');
|
|
10
|
+
|
|
3
11
|
const sqldb = require('../models');
|
|
12
|
+
const {
|
|
13
|
+
queue,
|
|
14
|
+
addJob,
|
|
15
|
+
jobStatus,
|
|
16
|
+
} = require('../worker/queue');
|
|
4
17
|
|
|
5
18
|
const { env: { jwtSecret } } = process;
|
|
6
19
|
|
|
@@ -42,6 +55,61 @@ const createAppToken = async (req, res, next) => {
|
|
|
42
55
|
});
|
|
43
56
|
if (userAppKeyDup) await userAppKeyDup.destroy();
|
|
44
57
|
const newAppKey = await sqldb.UserAppKey.create(payload);
|
|
58
|
+
// create any cronjobs related to the app
|
|
59
|
+
await bb.each(req.app.plugins, async plugin => {
|
|
60
|
+
if (Array.isArray(plugin.jobs)) {
|
|
61
|
+
await bb.each(plugin.jobs.filter(job => Boolean(job.cron)), async job => {
|
|
62
|
+
const where = {
|
|
63
|
+
pluginName: plugin.name,
|
|
64
|
+
pluginJobId: job.method,
|
|
65
|
+
userId,
|
|
66
|
+
hint,
|
|
67
|
+
cron: job.cron,
|
|
68
|
+
};
|
|
69
|
+
const existing = await sqldb.CronJobs.findOne({
|
|
70
|
+
where: R.omit(['cron'], where),
|
|
71
|
+
});
|
|
72
|
+
const jobPayload = {
|
|
73
|
+
...where,
|
|
74
|
+
...job.payload,
|
|
75
|
+
};
|
|
76
|
+
const jobParams = {
|
|
77
|
+
...(job.cron ? {
|
|
78
|
+
repeat: {
|
|
79
|
+
cron: job.cron,
|
|
80
|
+
},
|
|
81
|
+
} : {}),
|
|
82
|
+
...(job.params || {}),
|
|
83
|
+
removeOnComplete: false,
|
|
84
|
+
};
|
|
85
|
+
let bullJobId;
|
|
86
|
+
if (existing) {
|
|
87
|
+
const bullJob = await queue.getJob(job.bullJobId);
|
|
88
|
+
if (!bullJob) {
|
|
89
|
+
bullJobId = await addJob(jobPayload, jobParams);
|
|
90
|
+
existing.bullJobId = bullJobId;
|
|
91
|
+
await existing.save();
|
|
92
|
+
} else {
|
|
93
|
+
// make sure the cron is the same
|
|
94
|
+
const bullCron = R.path(
|
|
95
|
+
['opts', 'repeat', 'cron'],
|
|
96
|
+
await queue.getJob(bullJobId),
|
|
97
|
+
);
|
|
98
|
+
if (bullCron !== job.cron) {
|
|
99
|
+
await queue.removeJobs(bullJobId);
|
|
100
|
+
bullJobId = await addJob(jobPayload, jobParams);
|
|
101
|
+
existing.bullJobId = bullJobId;
|
|
102
|
+
await existing.save();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
bullJobId = await addJob(jobPayload, jobParams);
|
|
107
|
+
await sqldb.CronJobs.create({ ...where, bullJobId });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
45
113
|
return res.json({ value: newAppKey.get('id').toString() });
|
|
46
114
|
} catch (err) {
|
|
47
115
|
return next(err);
|
|
@@ -83,9 +151,114 @@ const listAppTokens = async (req, res, next) => {
|
|
|
83
151
|
}
|
|
84
152
|
};
|
|
85
153
|
|
|
154
|
+
const migrateApp = async ({ integrationId, action }) => {
|
|
155
|
+
const { sequelize } = sqldb;
|
|
156
|
+
assert(integrationId);
|
|
157
|
+
assert(action);
|
|
158
|
+
const migrationsPath = path.join(
|
|
159
|
+
__dirname,
|
|
160
|
+
'../',
|
|
161
|
+
'../',
|
|
162
|
+
`ti2-${integrationId}`,
|
|
163
|
+
'migrations',
|
|
164
|
+
);
|
|
165
|
+
try {
|
|
166
|
+
await fs.access(migrationsPath);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
throw Error(`Could not find any migrations for ${integrationId} on ${migrationsPath}`);
|
|
169
|
+
}
|
|
170
|
+
const umzug = new Umzug({
|
|
171
|
+
migrations: {
|
|
172
|
+
glob: `${migrationsPath}/*.js`,
|
|
173
|
+
resolve: ({ name, path: migPath, context }) => {
|
|
174
|
+
const migration = require(migPath);
|
|
175
|
+
return {
|
|
176
|
+
name,
|
|
177
|
+
up: async () => migration.up(context, Sequelize),
|
|
178
|
+
down: async () => migration.down(context, Sequelize),
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
context: sequelize.getQueryInterface(),
|
|
183
|
+
storage: new SequelizeStorage({
|
|
184
|
+
sequelize,
|
|
185
|
+
tableName: `SequelizeMeta-${integrationId}`,
|
|
186
|
+
}),
|
|
187
|
+
logger: console,
|
|
188
|
+
});
|
|
189
|
+
if (action === 'migrate') {
|
|
190
|
+
return umzug.up();
|
|
191
|
+
}
|
|
192
|
+
if (action === 'revert') {
|
|
193
|
+
return umzug.down({ to: 0 });
|
|
194
|
+
}
|
|
195
|
+
throw Error('No recognized action');
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const getAppScheduledJobs = async ({
|
|
199
|
+
integrationId,
|
|
200
|
+
userId,
|
|
201
|
+
hint,
|
|
202
|
+
}) => {
|
|
203
|
+
const where = R.reject(R.isNil)({
|
|
204
|
+
pluginName: integrationId,
|
|
205
|
+
userId,
|
|
206
|
+
hint,
|
|
207
|
+
});
|
|
208
|
+
const jobs = await sqldb.CronJobs.findAll({
|
|
209
|
+
where,
|
|
210
|
+
raw: true,
|
|
211
|
+
});
|
|
212
|
+
return { jobs };
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const runAppJob = async (req, res, next) => {
|
|
216
|
+
const {
|
|
217
|
+
body: {
|
|
218
|
+
payload,
|
|
219
|
+
jobParams,
|
|
220
|
+
},
|
|
221
|
+
params: {
|
|
222
|
+
app: pluginName,
|
|
223
|
+
hint,
|
|
224
|
+
userId,
|
|
225
|
+
},
|
|
226
|
+
} = req;
|
|
227
|
+
try {
|
|
228
|
+
const bullJobId = await addJob({
|
|
229
|
+
...payload,
|
|
230
|
+
pluginName,
|
|
231
|
+
hint,
|
|
232
|
+
userId,
|
|
233
|
+
}, jobParams);
|
|
234
|
+
assert(bullJobId);
|
|
235
|
+
return res.json(await jobStatus({ jobId: bullJobId }));
|
|
236
|
+
} catch (err) {
|
|
237
|
+
return next(err);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const getJobStatus = async (req, res, next) => {
|
|
242
|
+
const {
|
|
243
|
+
params: {
|
|
244
|
+
jobId,
|
|
245
|
+
},
|
|
246
|
+
} = req;
|
|
247
|
+
try {
|
|
248
|
+
const returnValue = await jobStatus({ jobId });
|
|
249
|
+
return res.json(returnValue);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
return next(err);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
86
255
|
module.exports = {
|
|
87
|
-
jwtEncode,
|
|
88
256
|
createAppToken,
|
|
89
257
|
deleteAppToken,
|
|
258
|
+
getAppScheduledJobs,
|
|
259
|
+
getJobStatus,
|
|
260
|
+
jwtEncode,
|
|
90
261
|
listAppTokens,
|
|
262
|
+
migrateApp,
|
|
263
|
+
runAppJob,
|
|
91
264
|
};
|
package/controllers/ping.js
CHANGED
package/controllers/user.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
// controllers/user.js
|
|
2
|
-
const { omit
|
|
3
|
-
const assert = require('assert');
|
|
4
|
-
const Promise = require('bluebird');
|
|
5
|
-
const moment = require('moment');
|
|
2
|
+
const { omit } = require('ramda');
|
|
6
3
|
|
|
7
|
-
const { UserAppKey
|
|
8
|
-
// const { load } = require('../../plugins/index.js');
|
|
4
|
+
const { UserAppKey } = require('../models');
|
|
9
5
|
|
|
10
6
|
const userAppList = async (req, res, next) => {
|
|
11
7
|
const { params: { userId } } = req;
|
package/index.js
CHANGED
|
@@ -5,6 +5,9 @@ const swaggerUi = require('swagger-ui-express');
|
|
|
5
5
|
const yaml = require('js-yaml');
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const { pickBy } = require('ramda');
|
|
8
|
+
const bb = require('bluebird');
|
|
9
|
+
const { Op } = require('sequelize');
|
|
10
|
+
const R = require('ramda');
|
|
8
11
|
|
|
9
12
|
const schema = yaml.load(fs.readFileSync(`${__dirname}/api.yml`));
|
|
10
13
|
|
|
@@ -14,18 +17,20 @@ const adminController = require('./controllers/admin');
|
|
|
14
17
|
const appController = require('./controllers/app');
|
|
15
18
|
const userController = require('./controllers/user');
|
|
16
19
|
const bookingsController = require('./controllers/bookings');
|
|
20
|
+
const { Integration } = require('./models');
|
|
17
21
|
|
|
18
|
-
module.exports = ({
|
|
22
|
+
module.exports = async ({
|
|
19
23
|
apiDocs = true,
|
|
20
24
|
elasticLogIndex = 'apilog_ti2',
|
|
21
25
|
elasticLogsClient,
|
|
22
26
|
plugins: pluginsParam = {},
|
|
23
27
|
port: portParam,
|
|
24
28
|
startServer = true,
|
|
29
|
+
worker = false,
|
|
25
30
|
}) => {
|
|
26
31
|
const port = portParam || process.env.PORT || 10010;
|
|
27
|
-
|
|
28
|
-
const plugins = Object.entries(pluginsParam)
|
|
32
|
+
// create the plugin instances
|
|
33
|
+
const plugins = await bb.map(Object.entries(pluginsParam), async ([pluginName, Plugin]) => {
|
|
29
34
|
// pass all env variables
|
|
30
35
|
const pluginEnv = pickBy(
|
|
31
36
|
(_val, key) => key.substring(0, `ti2_${pluginName}`.length)
|
|
@@ -37,9 +42,34 @@ module.exports = ({
|
|
|
37
42
|
const nuName = attr.replace(/_/g, '-').replace(`ti2-${pluginName}-`, '');
|
|
38
43
|
params[nuName] = value;
|
|
39
44
|
});
|
|
40
|
-
|
|
45
|
+
const pluginInstance = await new Plugin({ name: pluginName, ...params });
|
|
46
|
+
return pluginInstance;
|
|
47
|
+
});
|
|
48
|
+
if (worker) {
|
|
49
|
+
return require('./worker/index')({ plugins });
|
|
50
|
+
}
|
|
51
|
+
// make sure all plugins have a DB entry
|
|
52
|
+
const pluginNames = plugins.map(R.prop('name'));
|
|
53
|
+
const matchedIntegrations = await Integration.findAll({
|
|
54
|
+
attributes: ['name'],
|
|
55
|
+
where: {
|
|
56
|
+
name: {
|
|
57
|
+
[Op.in]: pluginNames,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
raw: true,
|
|
41
61
|
});
|
|
42
|
-
|
|
62
|
+
const missingIntegrations = R.difference(pluginNames, matchedIntegrations.map(R.prop('name')));
|
|
63
|
+
if (missingIntegrations.length > 0) {
|
|
64
|
+
// need to crete the missing integrations
|
|
65
|
+
await Integration.bulkCreate(missingIntegrations.map(name => ({
|
|
66
|
+
name,
|
|
67
|
+
packageName: `ti2-${name}`,
|
|
68
|
+
adminEmail: `ti2+${name}@localhost.local`,
|
|
69
|
+
})));
|
|
70
|
+
}
|
|
71
|
+
// create the API Web server
|
|
72
|
+
const app = express();
|
|
43
73
|
const api = {
|
|
44
74
|
...pingController,
|
|
45
75
|
...adminController,
|
|
@@ -141,9 +171,12 @@ module.exports = ({
|
|
|
141
171
|
connect(app);
|
|
142
172
|
app.use(middleware.mock());
|
|
143
173
|
// global error Handling
|
|
144
|
-
app.use((err, req, res
|
|
174
|
+
app.use((err, req, res) => {
|
|
145
175
|
// console.log(req.headers.['X-Request-Id'], err);
|
|
146
176
|
res.status(err.status || 500);
|
|
177
|
+
if ((process.env.JEST_WORKER_ID)) {
|
|
178
|
+
console.debug(err);
|
|
179
|
+
}
|
|
147
180
|
return res.json({
|
|
148
181
|
message: err.message || 'Internal Error',
|
|
149
182
|
});
|
|
@@ -156,8 +189,11 @@ module.exports = ({
|
|
|
156
189
|
// first create a generic "terminator"
|
|
157
190
|
const terminator = sig => {
|
|
158
191
|
if (typeof sig === 'string') {
|
|
159
|
-
console.log(
|
|
160
|
-
|
|
192
|
+
console.log(
|
|
193
|
+
'%s: Received %s - terminating ti2 ...',
|
|
194
|
+
Date(Date.now()),
|
|
195
|
+
sig,
|
|
196
|
+
);
|
|
161
197
|
process.exit(1);
|
|
162
198
|
}
|
|
163
199
|
console.log('%s: Node server stopped.', Date(Date.now()));
|
package/jest.setup.js
ADDED
package/jsdoc.conf.json
CHANGED
|
@@ -3,8 +3,8 @@ module.exports = {
|
|
|
3
3
|
type: Sequelize.TEXT,
|
|
4
4
|
allowNull: false,
|
|
5
5
|
}),
|
|
6
|
-
down: (queryInterface, Sequelize) =>
|
|
6
|
+
down: (queryInterface, Sequelize) => queryInterface.changeColumn('UserAppKeys', 'appKey', {
|
|
7
7
|
type: Sequelize.STRING,
|
|
8
8
|
allowNull: false,
|
|
9
|
-
})
|
|
9
|
+
}),
|
|
10
10
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
up: (queryInterface, Sequelize) => queryInterface
|
|
3
|
+
.createTable('CronJobs', {
|
|
4
|
+
pluginName: {
|
|
5
|
+
allowNull: false,
|
|
6
|
+
primaryKey: true,
|
|
7
|
+
type: Sequelize.STRING,
|
|
8
|
+
},
|
|
9
|
+
userId: {
|
|
10
|
+
allowNull: false,
|
|
11
|
+
primaryKey: true,
|
|
12
|
+
type: Sequelize.STRING,
|
|
13
|
+
},
|
|
14
|
+
hint: {
|
|
15
|
+
allowNull: false,
|
|
16
|
+
primaryKey: true,
|
|
17
|
+
type: Sequelize.STRING,
|
|
18
|
+
},
|
|
19
|
+
pluginJobId: {
|
|
20
|
+
allowNull: false,
|
|
21
|
+
type: Sequelize.STRING,
|
|
22
|
+
},
|
|
23
|
+
bullJobId: {
|
|
24
|
+
allowNull: false,
|
|
25
|
+
type: Sequelize.STRING,
|
|
26
|
+
},
|
|
27
|
+
cron: {
|
|
28
|
+
allowNull: false,
|
|
29
|
+
type: Sequelize.STRING,
|
|
30
|
+
},
|
|
31
|
+
createdAt: {
|
|
32
|
+
allowNull: false,
|
|
33
|
+
type: Sequelize.DATE,
|
|
34
|
+
},
|
|
35
|
+
updatedAt: {
|
|
36
|
+
allowNull: false,
|
|
37
|
+
type: Sequelize.DATE,
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
down: queryInterface => queryInterface.dropTable('CronJobs'),
|
|
41
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const Sequelize = require('sequelize');
|
|
2
|
+
const db = require('./db');
|
|
3
|
+
|
|
4
|
+
const CronJobs = db.define('CronJobs', {
|
|
5
|
+
pluginName: {
|
|
6
|
+
type: Sequelize.STRING,
|
|
7
|
+
primaryKey: true,
|
|
8
|
+
},
|
|
9
|
+
userId: {
|
|
10
|
+
type: Sequelize.STRING,
|
|
11
|
+
primaryKey: true,
|
|
12
|
+
},
|
|
13
|
+
hint: {
|
|
14
|
+
type: Sequelize.STRING,
|
|
15
|
+
primaryKey: true,
|
|
16
|
+
},
|
|
17
|
+
pluginJobId: {
|
|
18
|
+
type: Sequelize.STRING,
|
|
19
|
+
primaryKey: true,
|
|
20
|
+
},
|
|
21
|
+
bullJobId: {
|
|
22
|
+
type: Sequelize.STRING,
|
|
23
|
+
allowNull: false,
|
|
24
|
+
},
|
|
25
|
+
cron: {
|
|
26
|
+
type: Sequelize.STRING,
|
|
27
|
+
allowNull: false,
|
|
28
|
+
},
|
|
29
|
+
}, {});
|
|
30
|
+
|
|
31
|
+
module.exports = CronJobs;
|
package/models/db.js
CHANGED
package/models/index.js
CHANGED
|
@@ -10,6 +10,7 @@ fs
|
|
|
10
10
|
.readdirSync(__dirname)
|
|
11
11
|
.filter(file => (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'))
|
|
12
12
|
.forEach(file => {
|
|
13
|
+
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
13
14
|
const model = require(path.join(__dirname, file));
|
|
14
15
|
models[model.name] = model;
|
|
15
16
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ti2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Tourist Industry Exchange (TI2)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -8,19 +8,28 @@
|
|
|
8
8
|
"sequelize": "sequelize-cli",
|
|
9
9
|
"doc": "jsdoc -c jsdoc.conf.json -u tutorials"
|
|
10
10
|
},
|
|
11
|
+
"jest": {
|
|
12
|
+
"setupFilesAfterEnv": [
|
|
13
|
+
"./jest.setup.js"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
11
16
|
"repository": {
|
|
12
17
|
"type": "git",
|
|
13
18
|
"url": "git+https://github.com/ti2travel/ti2.git"
|
|
14
19
|
},
|
|
20
|
+
"bin": "./cli.js",
|
|
15
21
|
"author": "",
|
|
16
|
-
"license": "GPL-3",
|
|
22
|
+
"license": "GPL-3.0",
|
|
17
23
|
"bugs": {
|
|
18
24
|
"url": "https://github.com/ti2travel/ti2/issues"
|
|
19
25
|
},
|
|
20
26
|
"homepage": "https://github.com/ti2travel/ti2#readme",
|
|
21
27
|
"dependencies": {
|
|
22
28
|
"bluebird": "^3.7.2",
|
|
29
|
+
"bull": "^4.8.2",
|
|
30
|
+
"commander": "^9.2.0",
|
|
23
31
|
"express": "^4.17.2",
|
|
32
|
+
"ioredis": "^5.0.4",
|
|
24
33
|
"js-yaml": "^4.1.0",
|
|
25
34
|
"jsonwebtoken": "^8.5.1",
|
|
26
35
|
"jwt-promise": "^1.0.0",
|
|
@@ -32,16 +41,18 @@
|
|
|
32
41
|
"swagger-express-middleware": "3.0.0-alpha.5",
|
|
33
42
|
"swagger-routes-express": "3.3.1",
|
|
34
43
|
"swagger-ui-express": "4.3.0",
|
|
44
|
+
"throng": "^5.0.0",
|
|
45
|
+
"umzug": "^3.1.1",
|
|
35
46
|
"uninstall": "^0.0.0"
|
|
36
47
|
},
|
|
37
48
|
"devDependencies": {
|
|
49
|
+
"@salvador/minami": "^1.2.3",
|
|
38
50
|
"chance": "^1.1.8",
|
|
39
51
|
"eslint": "^8.7.0",
|
|
40
52
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
41
53
|
"eslint-plugin-import": "^2.25.4",
|
|
42
54
|
"jest": "^27.4.7",
|
|
43
55
|
"jsdoc": "^3.6.10",
|
|
44
|
-
"minami": "git+https://github.com/MyNameReallySux/minami.git#sort-data",
|
|
45
56
|
"supertest": "^6.2.2"
|
|
46
57
|
}
|
|
47
58
|
}
|
package/test/plugin.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
/* global jest, expect */
|
|
2
|
+
/* eslint class-methods-use-this: "off", max-len: "off" */
|
|
3
|
+
|
|
4
|
+
const jestPlugin = (() => {
|
|
5
|
+
if (typeof jest === 'undefined') {
|
|
6
|
+
return {
|
|
7
|
+
fn: fn => fn,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return jest;
|
|
11
|
+
})();
|
|
12
|
+
|
|
2
13
|
const chance = require('chance').Chance();
|
|
3
14
|
|
|
4
15
|
class Plugin {
|
|
@@ -15,16 +26,16 @@ class Plugin {
|
|
|
15
26
|
this[attr] = value;
|
|
16
27
|
});
|
|
17
28
|
// mock implementations
|
|
18
|
-
this.validateToken =
|
|
19
|
-
this.getProfile =
|
|
20
|
-
this.updateProfile =
|
|
21
|
-
this.getProduct =
|
|
22
|
-
this.getProducts =
|
|
23
|
-
this.createLocation =
|
|
24
|
-
this.updateLocation =
|
|
25
|
-
this.searchBooking =
|
|
26
|
-
this.searchProducts =
|
|
27
|
-
this.searchAvailability =
|
|
29
|
+
this.validateToken = jestPlugin.fn(() => true);
|
|
30
|
+
this.getProfile = jestPlugin.fn(() => {});
|
|
31
|
+
this.updateProfile = jestPlugin.fn(() => {});
|
|
32
|
+
this.getProduct = jestPlugin.fn(() => ({ products: [] }));
|
|
33
|
+
this.getProducts = jestPlugin.fn(() => true);
|
|
34
|
+
this.createLocation = jestPlugin.fn(() => ({ locationId: chance.guid() }));
|
|
35
|
+
this.updateLocation = jestPlugin.fn(() => true);
|
|
36
|
+
this.searchBooking = jestPlugin.fn(() => ({ bookings: [] }));
|
|
37
|
+
this.searchProducts = jestPlugin.fn(() => ({ bookings: [], products: [] }));
|
|
38
|
+
this.searchAvailability = jestPlugin.fn(({
|
|
28
39
|
token,
|
|
29
40
|
payload: {
|
|
30
41
|
travelDateStart,
|
|
@@ -40,9 +51,23 @@ class Plugin {
|
|
|
40
51
|
expect(Array.isArray(occupancies)).toBeTruthy();
|
|
41
52
|
return { availability: [{ id: chance.guid() }] };
|
|
42
53
|
});
|
|
54
|
+
this.searchQuote = jestPlugin.fn(() => ({ quote: [{ id: chance.guid() }] }));
|
|
55
|
+
this.createBooking = jestPlugin.fn(() => {});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Background and schedule Jos
|
|
59
|
+
*/
|
|
60
|
+
this.jobs = [{
|
|
61
|
+
method: 'dailyReport',
|
|
62
|
+
payload: {
|
|
63
|
+
action: 'report',
|
|
64
|
+
},
|
|
65
|
+
cron: '0 9 * * *',
|
|
66
|
+
}];
|
|
43
67
|
|
|
44
|
-
this.
|
|
45
|
-
|
|
68
|
+
this.dailyReport = jestPlugin.fn(() => ({
|
|
69
|
+
someVal: true,
|
|
70
|
+
}));
|
|
46
71
|
}
|
|
47
72
|
|
|
48
73
|
/**
|
|
@@ -258,7 +283,7 @@ class Plugin {
|
|
|
258
283
|
return true;
|
|
259
284
|
}
|
|
260
285
|
|
|
261
|
-
searchHotelBooking =
|
|
286
|
+
searchHotelBooking = jestPlugin.fn(() => ({ bookings: [] }));
|
|
262
287
|
|
|
263
288
|
/**
|
|
264
289
|
* Booking Object
|
|
@@ -363,6 +388,7 @@ class Plugin {
|
|
|
363
388
|
searchAvailability() {
|
|
364
389
|
// return (args);
|
|
365
390
|
}
|
|
391
|
+
|
|
366
392
|
/**
|
|
367
393
|
* Retrieve an availability calendar over a date range
|
|
368
394
|
* @async
|
|
@@ -379,6 +405,7 @@ class Plugin {
|
|
|
379
405
|
availabilityCalndar() {
|
|
380
406
|
// return (args);
|
|
381
407
|
}
|
|
408
|
+
|
|
382
409
|
/**
|
|
383
410
|
* A quote return spec
|
|
384
411
|
* @typedef {Object} Quote
|
package/test/utils.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const chance = require('chance').Chance();
|
|
4
4
|
const request = require('supertest');
|
|
5
5
|
const assert = require('assert');
|
|
6
|
+
|
|
6
7
|
const appReq = require('../index');
|
|
7
8
|
const Plugin = require('./plugin');
|
|
8
9
|
const slugify = require('./slugify');
|
|
@@ -10,12 +11,10 @@ const sqldb = require('../models/db');
|
|
|
10
11
|
|
|
11
12
|
const { env: { adminKey } } = process;
|
|
12
13
|
|
|
13
|
-
const timeout = ms => (new Promise(resolve => setTimeout(resolve, ms)));
|
|
14
|
-
|
|
15
14
|
afterAll(async () => {
|
|
16
15
|
await sqldb.connectionManager.close();
|
|
17
16
|
});
|
|
18
|
-
module.exports = (appParams = {}) => {
|
|
17
|
+
module.exports = async (appParams = {}) => {
|
|
19
18
|
const plugins = (() => {
|
|
20
19
|
if (Array.isArray(appParams.plugins)) {
|
|
21
20
|
const retVal = {};
|
|
@@ -28,7 +27,7 @@ module.exports = (appParams = {}) => {
|
|
|
28
27
|
mockName: Plugin,
|
|
29
28
|
};
|
|
30
29
|
})();
|
|
31
|
-
const app = appReq({
|
|
30
|
+
const app = await appReq({
|
|
32
31
|
startServer: false,
|
|
33
32
|
...appParams,
|
|
34
33
|
plugins,
|
|
@@ -104,7 +103,6 @@ module.exports = (appParams = {}) => {
|
|
|
104
103
|
doApiPost,
|
|
105
104
|
doApiPut,
|
|
106
105
|
slugify,
|
|
107
|
-
timeout,
|
|
108
106
|
plugins: app.plugins,
|
|
109
107
|
};
|
|
110
108
|
};
|
package/test/worker.js
ADDED
|
@@ -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
|
-
$
|
|
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
|
|
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
|
|