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/.eslintrc.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
name: Tests Workflow
|
|
2
2
|
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
push:
|
|
3
|
+
on: [workflow_dispatch, push, pull_request]
|
|
6
4
|
|
|
7
5
|
jobs:
|
|
8
6
|
test:
|
|
9
7
|
timeout-minutes: 10
|
|
10
8
|
runs-on: ubuntu-latest
|
|
11
9
|
container:
|
|
12
|
-
image: node:
|
|
10
|
+
image: node:16.15.0
|
|
13
11
|
services:
|
|
14
12
|
mysql:
|
|
15
13
|
image: mysql
|
|
@@ -19,9 +17,12 @@ jobs:
|
|
|
19
17
|
MYSQL_ROOT_PASSWORD: root
|
|
20
18
|
ports:
|
|
21
19
|
- 3306:3306
|
|
20
|
+
redis:
|
|
21
|
+
image: redis
|
|
22
22
|
env:
|
|
23
23
|
CI: true
|
|
24
24
|
DB_URL: 'mysql://root:root@mysql/ti2development'
|
|
25
|
+
REDIS_URL: 'redis://redis:6379'
|
|
25
26
|
dbCryptoKey: 'YmYzYzE5OWMyNDcwY2I0NzdkOTA3YjFlMDkxN2MxN2I='
|
|
26
27
|
jwtSecret: '9rifawckwztuKehocJOtGM6deTqICGWzIyc5CqgNyi'
|
|
27
28
|
frontendKey: 'lYjEJHzb7lbQ1hk3ypjX'
|
|
@@ -32,10 +33,11 @@ jobs:
|
|
|
32
33
|
with:
|
|
33
34
|
node-version: 12
|
|
34
35
|
- name: install dependencies
|
|
35
|
-
run: npm
|
|
36
|
+
run: npm ci
|
|
36
37
|
- name: create/migrate the database
|
|
37
38
|
run: npx sequelize db:create && npx sequelize db:migrate
|
|
38
39
|
- name: seed the database
|
|
39
40
|
run: npx sequelize db:seed:all
|
|
41
|
+
- run: RUNNER_TRACKING_ID="" && (nohup node ./test/worker.js&)
|
|
40
42
|
- name: run the tests
|
|
41
43
|
run: npx jest -i --coverage --forceExit
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v16.15.0
|
package/api.yml
CHANGED
|
@@ -29,6 +29,34 @@ components:
|
|
|
29
29
|
properties:
|
|
30
30
|
value:
|
|
31
31
|
type: string
|
|
32
|
+
JobSpec:
|
|
33
|
+
type: object
|
|
34
|
+
properties:
|
|
35
|
+
payload:
|
|
36
|
+
type: object
|
|
37
|
+
description: The job app parameters
|
|
38
|
+
required: true
|
|
39
|
+
example: '{
|
|
40
|
+
"method": "dailyReport"
|
|
41
|
+
}'
|
|
42
|
+
jobParams:
|
|
43
|
+
type: object
|
|
44
|
+
description: Additional bull job params
|
|
45
|
+
example: '{
|
|
46
|
+
"delay": "5000"
|
|
47
|
+
}'
|
|
48
|
+
JobStatus:
|
|
49
|
+
type: object
|
|
50
|
+
properties:
|
|
51
|
+
jobId:
|
|
52
|
+
type: string
|
|
53
|
+
status:
|
|
54
|
+
type: string
|
|
55
|
+
example: 'active'
|
|
56
|
+
result:
|
|
57
|
+
type: object
|
|
58
|
+
description: The job return values
|
|
59
|
+
example: '{ "result": "value" }'
|
|
32
60
|
App:
|
|
33
61
|
type: object
|
|
34
62
|
properties:
|
|
@@ -534,6 +562,128 @@ paths:
|
|
|
534
562
|
content:
|
|
535
563
|
application/json:
|
|
536
564
|
schema: '#/components/schemas/Message'
|
|
565
|
+
|
|
566
|
+
/{app}/{userId}/jobRun:
|
|
567
|
+
security:
|
|
568
|
+
- bearerAuth: ['app', 'user', 'admin']
|
|
569
|
+
post:
|
|
570
|
+
tags:
|
|
571
|
+
- jobs
|
|
572
|
+
summary: Run a Job
|
|
573
|
+
security:
|
|
574
|
+
- bearerAuth: ['app', 'user', 'admin']
|
|
575
|
+
operationId: runAppJob
|
|
576
|
+
parameters:
|
|
577
|
+
- name: app
|
|
578
|
+
in: path
|
|
579
|
+
description: app that defines the job
|
|
580
|
+
example: 'ventrata'
|
|
581
|
+
required: true
|
|
582
|
+
schema:
|
|
583
|
+
type: string
|
|
584
|
+
- name: userId
|
|
585
|
+
in: path
|
|
586
|
+
description: user to run the schedule app against
|
|
587
|
+
example: '536830b6ed19afa44a000002'
|
|
588
|
+
required: true
|
|
589
|
+
schema:
|
|
590
|
+
type: string
|
|
591
|
+
requestBody:
|
|
592
|
+
description: The job parameters
|
|
593
|
+
required: true
|
|
594
|
+
content:
|
|
595
|
+
application/json:
|
|
596
|
+
schema:
|
|
597
|
+
$ref: '#/components/schemas/JobSpec'
|
|
598
|
+
responses:
|
|
599
|
+
200:
|
|
600
|
+
description: success
|
|
601
|
+
content:
|
|
602
|
+
application/json:
|
|
603
|
+
schema: '#/components/schemas/JobStatus'
|
|
604
|
+
/{app}/{userId}/{hint}/jobRun:
|
|
605
|
+
security:
|
|
606
|
+
- bearerAuth: ['app', 'user', 'admin']
|
|
607
|
+
post:
|
|
608
|
+
tags:
|
|
609
|
+
- jobs
|
|
610
|
+
summary: Run a schedule Job
|
|
611
|
+
security:
|
|
612
|
+
- bearerAuth: ['app', 'user', 'admin']
|
|
613
|
+
operationId: runAppJob
|
|
614
|
+
parameters:
|
|
615
|
+
- name: app
|
|
616
|
+
in: path
|
|
617
|
+
description: app that defines the job
|
|
618
|
+
example: 'ventrata'
|
|
619
|
+
required: true
|
|
620
|
+
schema:
|
|
621
|
+
type: string
|
|
622
|
+
- name: userId
|
|
623
|
+
in: path
|
|
624
|
+
description: user to run the schedule app against
|
|
625
|
+
example: '536830b6ed19afa44a000002'
|
|
626
|
+
required: true
|
|
627
|
+
schema:
|
|
628
|
+
type: string
|
|
629
|
+
- name: hint
|
|
630
|
+
in: path
|
|
631
|
+
description: a specific token hint to be used
|
|
632
|
+
required: true
|
|
633
|
+
example: 'travelgate-australia'
|
|
634
|
+
schema:
|
|
635
|
+
type: string
|
|
636
|
+
requestBody:
|
|
637
|
+
description: The job parameters
|
|
638
|
+
required: true
|
|
639
|
+
content:
|
|
640
|
+
application/json:
|
|
641
|
+
schema:
|
|
642
|
+
$ref: '#/components/schemas/JobSpec'
|
|
643
|
+
responses:
|
|
644
|
+
200:
|
|
645
|
+
description: success
|
|
646
|
+
content:
|
|
647
|
+
application/json:
|
|
648
|
+
schema: '#/components/schemas/JobStatus'
|
|
649
|
+
/{app}/{userId}/{jobId}/jobStatus:
|
|
650
|
+
security:
|
|
651
|
+
- bearerAuth: ['app', 'user', 'admin']
|
|
652
|
+
get:
|
|
653
|
+
tags:
|
|
654
|
+
- jobs
|
|
655
|
+
summary: Get the status and/or result from a job
|
|
656
|
+
security:
|
|
657
|
+
- bearerAuth: ['app', 'user', 'admin']
|
|
658
|
+
operationId: getJobStatus
|
|
659
|
+
parameters:
|
|
660
|
+
- name: app
|
|
661
|
+
in: path
|
|
662
|
+
description: app that defines the job
|
|
663
|
+
example: 'ventrata'
|
|
664
|
+
required: true
|
|
665
|
+
schema:
|
|
666
|
+
type: string
|
|
667
|
+
- name: userId
|
|
668
|
+
in: path
|
|
669
|
+
description: user that owns the job
|
|
670
|
+
example: '536830b6ed19afa44a000002'
|
|
671
|
+
required: true
|
|
672
|
+
schema:
|
|
673
|
+
type: string
|
|
674
|
+
- name: jobId
|
|
675
|
+
in: path
|
|
676
|
+
description: jobId to get the status for
|
|
677
|
+
example: '536830b6ed19afa44a000002'
|
|
678
|
+
required: true
|
|
679
|
+
schema:
|
|
680
|
+
type: string
|
|
681
|
+
responses:
|
|
682
|
+
200:
|
|
683
|
+
description: success
|
|
684
|
+
content:
|
|
685
|
+
application/json:
|
|
686
|
+
schema: '#/components/schemas/JobStatus'
|
|
537
687
|
/user/{userId}/apps:
|
|
538
688
|
get:
|
|
539
689
|
security:
|
package/auth/authHandler.js
CHANGED
|
@@ -68,10 +68,10 @@ const frontend = async (req, res, next) => {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
const adminOrUser = levels => async (req, res, next) => {
|
|
71
|
-
result = await Promise.all(levels.map(
|
|
71
|
+
const result = await Promise.all(levels.map(
|
|
72
72
|
level => level(req, res, err => err),
|
|
73
73
|
));
|
|
74
|
-
if (result.some(val => (!
|
|
74
|
+
if (result.some(val => (!val))) {
|
|
75
75
|
return next();
|
|
76
76
|
}
|
|
77
77
|
return next(result[0]);
|
package/cli.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command, Argument } = require('commander');
|
|
4
|
+
|
|
5
|
+
const { migrateApp } = require('./controllers/app');
|
|
6
|
+
const { migrate } = require('./controllers/admin');
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('ti2-cli')
|
|
12
|
+
.description('TI2 Command line interface utilities')
|
|
13
|
+
.version((require('./package.json')).version);
|
|
14
|
+
|
|
15
|
+
program.command('dbapp')
|
|
16
|
+
.description('Handle database migrations for apps')
|
|
17
|
+
.argument('<string>', 'integration to operate on')
|
|
18
|
+
.addArgument(
|
|
19
|
+
new Argument('<action>', 'action to take', 'migrate')
|
|
20
|
+
.choices(['migrate', 'revert']),
|
|
21
|
+
)
|
|
22
|
+
.action(async (integration, action) => {
|
|
23
|
+
await migrateApp({ integrationId: integration, action });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
program.command('db')
|
|
27
|
+
.description('Handle database migrations for integrations')
|
|
28
|
+
.addArgument(
|
|
29
|
+
new Argument('<action>', 'action to take', 'migrate')
|
|
30
|
+
.choices(['migrate', 'revert']),
|
|
31
|
+
)
|
|
32
|
+
.action(async action => {
|
|
33
|
+
await migrate({ action });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
program.parse();
|
package/closeAll.js
ADDED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* globals describe it expect */
|
|
1
|
+
/* globals beforeAll describe it expect */
|
|
2
2
|
|
|
3
3
|
const chance = require('chance').Chance();
|
|
4
4
|
const testUtils = require('../../test/utils');
|
|
@@ -15,11 +15,18 @@ describe('admin', () => {
|
|
|
15
15
|
packageName: `ti2-${appName}`,
|
|
16
16
|
adminEmail: chance.email(),
|
|
17
17
|
};
|
|
18
|
-
const { doApiPost, doApiGet } = testUtils({
|
|
19
|
-
plugins: [appName],
|
|
20
|
-
});
|
|
21
18
|
let appKey;
|
|
22
19
|
const userId = '536830b6ed19afa44a000002';
|
|
20
|
+
let doApiPost; let
|
|
21
|
+
doApiGet;
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
({
|
|
24
|
+
doApiGet,
|
|
25
|
+
doApiPost,
|
|
26
|
+
} = await testUtils({
|
|
27
|
+
plugins: [appName],
|
|
28
|
+
}));
|
|
29
|
+
});
|
|
23
30
|
it('should be able to create an app', async () => {
|
|
24
31
|
({ value: appKey } = await doApiPost({
|
|
25
32
|
url: '/app',
|
|
@@ -33,11 +40,7 @@ describe('admin', () => {
|
|
|
33
40
|
url: '/apps',
|
|
34
41
|
token: adminKey,
|
|
35
42
|
});
|
|
36
|
-
expect(integrations).
|
|
37
|
-
expect.arrayContaining([
|
|
38
|
-
expect.objectContaining(newApp),
|
|
39
|
-
]),
|
|
40
|
-
);
|
|
43
|
+
expect(integrations.find(({ name }) => name === newApp.name)).toBeTruthy();
|
|
41
44
|
});
|
|
42
45
|
it('should be able to reset an app\'s key', async () => {
|
|
43
46
|
const url = `/app/resetAppKey/${newApp.name}`;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
/* globals describe it expect */
|
|
2
|
-
|
|
1
|
+
/* globals beforeAll describe it expect */
|
|
3
2
|
const chance = require('chance').Chance();
|
|
4
3
|
const jwt = require('jwt-promise');
|
|
4
|
+
const R = require('ramda');
|
|
5
|
+
|
|
5
6
|
const testUtils = require('../../test/utils');
|
|
6
7
|
const slugify = require('../../test/slugify');
|
|
8
|
+
const appController = require('../app');
|
|
7
9
|
|
|
8
10
|
const { env: { adminKey, jwtSecret } } = process;
|
|
9
11
|
|
|
@@ -16,9 +18,9 @@ describe('app', () => {
|
|
|
16
18
|
packageName: `ti2-${appName}`,
|
|
17
19
|
adminEmail: chance.email(),
|
|
18
20
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
let doApiPost;
|
|
22
|
+
let doApiGet;
|
|
23
|
+
let doApiPut;
|
|
22
24
|
let appKey;
|
|
23
25
|
const userId = chance.guid();
|
|
24
26
|
const apiKey = chance.guid();
|
|
@@ -31,6 +33,11 @@ describe('app', () => {
|
|
|
31
33
|
payload: { lorem: chance.paragraph() },
|
|
32
34
|
};
|
|
33
35
|
let encodedKey;
|
|
36
|
+
beforeAll(async () => {
|
|
37
|
+
({ doApiPost, doApiGet, doApiPut } = await testUtils({
|
|
38
|
+
plugins: [appName],
|
|
39
|
+
}));
|
|
40
|
+
});
|
|
34
41
|
it('should create a new app', async () => {
|
|
35
42
|
({ value: appKey } = await doApiPost({
|
|
36
43
|
url: '/app',
|
|
@@ -62,6 +69,51 @@ describe('app', () => {
|
|
|
62
69
|
});
|
|
63
70
|
expect(parseInt(value, 10)).toBeGreaterThan(0);
|
|
64
71
|
});
|
|
72
|
+
describe('jobs', () => {
|
|
73
|
+
it('the scheduled job should have been created for the user', async () => {
|
|
74
|
+
const jobs = R.path(
|
|
75
|
+
['jobs'],
|
|
76
|
+
await appController.getAppScheduledJobs({
|
|
77
|
+
integrationId: appName,
|
|
78
|
+
userId,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
expect(Array.isArray(jobs)).toBeTruthy();
|
|
82
|
+
expect(R.head(R.project(['pluginJobId', 'cron'], jobs)))
|
|
83
|
+
.toEqual({
|
|
84
|
+
pluginJobId: 'dailyReport',
|
|
85
|
+
cron: '0 9 * * *',
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
it.todo('if a new version of the plugin has a different set of scheduled tasks, they shoul dbe re-syncjed');
|
|
89
|
+
let jobId;
|
|
90
|
+
it('should be able to run a job', async () => {
|
|
91
|
+
let status;
|
|
92
|
+
({ jobId, status } = await doApiPost({
|
|
93
|
+
url: `/${appName}/${userId}/jobRun`,
|
|
94
|
+
token: appKey,
|
|
95
|
+
payload: {
|
|
96
|
+
payload: {
|
|
97
|
+
method: 'dailyReport',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}));
|
|
101
|
+
expect(jobId).toBeTruthy();
|
|
102
|
+
expect(status).toBeTruthy();
|
|
103
|
+
});
|
|
104
|
+
it('should be able to get the status of a ran job', async () => {
|
|
105
|
+
await global.sleep(5e3);
|
|
106
|
+
const url = `/${appName}/${userId}/${jobId}/jobStatus`;
|
|
107
|
+
const jobStatus = await doApiGet({
|
|
108
|
+
url,
|
|
109
|
+
token: appKey,
|
|
110
|
+
payload: {},
|
|
111
|
+
});
|
|
112
|
+
expect(jobStatus.jobId).toBeTruthy();
|
|
113
|
+
expect(jobStatus.status).toBe('success');
|
|
114
|
+
expect(jobStatus.result).toEqual({ someVal: true });
|
|
115
|
+
}, 6e3);
|
|
116
|
+
});
|
|
65
117
|
it('should be able to get a list of all tokens related to the app', async () => {
|
|
66
118
|
const { userAppKeys } = await doApiGet({
|
|
67
119
|
url: `/app/tokens/${appName}`,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* globals describe it expect */
|
|
1
|
+
/* globals beforeAll describe it expect */
|
|
2
2
|
|
|
3
3
|
const chance = require('chance').Chance();
|
|
4
4
|
const testUtils = require('../../test/utils');
|
|
@@ -22,13 +22,19 @@ describe('user: booking search', () => {
|
|
|
22
22
|
tokenHint: 'testingToken',
|
|
23
23
|
token,
|
|
24
24
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
let doApiDelete;
|
|
26
|
+
let doApiGet;
|
|
27
|
+
let doApiPost;
|
|
28
|
+
let plugins;
|
|
29
|
+
beforeAll(async () => {
|
|
30
|
+
({
|
|
31
|
+
doApiDelete,
|
|
32
|
+
doApiGet,
|
|
33
|
+
doApiPost,
|
|
34
|
+
plugins,
|
|
35
|
+
} = await testUtils({
|
|
36
|
+
plugins: [newApp.name],
|
|
37
|
+
}));
|
|
32
38
|
});
|
|
33
39
|
|
|
34
40
|
let userToken;
|
|
@@ -39,13 +45,17 @@ describe('user: booking search', () => {
|
|
|
39
45
|
token: adminKey,
|
|
40
46
|
payload: { tokenHint: 'testingToken' },
|
|
41
47
|
});
|
|
42
|
-
} catch {
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.debug(err);
|
|
50
|
+
}
|
|
43
51
|
try {
|
|
44
52
|
await doApiDelete({
|
|
45
53
|
url: '/app/travelgate',
|
|
46
54
|
token: adminKey,
|
|
47
55
|
});
|
|
48
|
-
} catch {
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.debug(err);
|
|
58
|
+
}
|
|
49
59
|
});
|
|
50
60
|
it('create the travelgate app', async () => {
|
|
51
61
|
const { value: apiKey } = await doApiPost({
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
/* global describe, it, expect */
|
|
2
|
-
|
|
3
|
-
const { doApiGet } = require('../../test/utils')();
|
|
1
|
+
/* global beforeAll, describe, it, expect */
|
|
2
|
+
const testUtils = require('../../test/utils');
|
|
4
3
|
|
|
5
4
|
describe('ping', () => {
|
|
5
|
+
let doApiGet;
|
|
6
|
+
beforeAll(async () => {
|
|
7
|
+
({
|
|
8
|
+
doApiGet,
|
|
9
|
+
} = await testUtils());
|
|
10
|
+
});
|
|
6
11
|
it('should get a server pong', async () => {
|
|
7
12
|
const response = await doApiGet({
|
|
8
13
|
url: '/ping',
|
|
@@ -15,9 +15,8 @@ describe('user', () => {
|
|
|
15
15
|
packageName: `ti2-${appName}`,
|
|
16
16
|
adminEmail: chance.email(),
|
|
17
17
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
18
|
+
let doApiGet; let doApiPost; let
|
|
19
|
+
doApiDelete;
|
|
21
20
|
let appKey;
|
|
22
21
|
const userId = chance.guid();
|
|
23
22
|
let apiKey = chance.guid();
|
|
@@ -27,6 +26,13 @@ describe('user', () => {
|
|
|
27
26
|
apiKey,
|
|
28
27
|
};
|
|
29
28
|
beforeAll(async () => {
|
|
29
|
+
({
|
|
30
|
+
doApiDelete,
|
|
31
|
+
doApiGet,
|
|
32
|
+
doApiPost,
|
|
33
|
+
} = await testUtils({
|
|
34
|
+
plugins: [appName],
|
|
35
|
+
}));
|
|
30
36
|
// create an App
|
|
31
37
|
({ value: appKey } = await doApiPost({
|
|
32
38
|
url: '/app',
|
package/controllers/admin.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const jwt = require('jsonwebtoken');
|
|
2
2
|
const assert = require('assert');
|
|
3
3
|
const { omit } = require('ramda');
|
|
4
|
+
const { Umzug, SequelizeStorage } = require('umzug');
|
|
5
|
+
const Sequelize = require('sequelize');
|
|
6
|
+
const fs = require('fs').promises;
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
4
9
|
const sqldb = require('../models');
|
|
5
10
|
|
|
6
11
|
const { env: { jwtSecret } } = process;
|
|
@@ -91,10 +96,50 @@ const resetIntegrationToken = async (req, res, next) => {
|
|
|
91
96
|
}
|
|
92
97
|
};
|
|
93
98
|
|
|
99
|
+
const migrate = async ({ action }) => {
|
|
100
|
+
const { sequelize } = sqldb;
|
|
101
|
+
const migrationsPath = path.join(
|
|
102
|
+
__dirname,
|
|
103
|
+
'../',
|
|
104
|
+
'migrations',
|
|
105
|
+
);
|
|
106
|
+
try {
|
|
107
|
+
await fs.access(migrationsPath);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw Error(`Could not find any migrations on ${migrationsPath}`);
|
|
110
|
+
}
|
|
111
|
+
const umzug = new Umzug({
|
|
112
|
+
migrations: {
|
|
113
|
+
glob: `${migrationsPath}/*.js`,
|
|
114
|
+
resolve: ({ name, path: migPath, context }) => {
|
|
115
|
+
const migration = require(migPath);
|
|
116
|
+
return {
|
|
117
|
+
name,
|
|
118
|
+
up: async () => migration.up(context, Sequelize),
|
|
119
|
+
down: async () => migration.down(context, Sequelize),
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
context: sequelize.getQueryInterface(),
|
|
124
|
+
storage: new SequelizeStorage({
|
|
125
|
+
sequelize,
|
|
126
|
+
}),
|
|
127
|
+
logger: console,
|
|
128
|
+
});
|
|
129
|
+
if (action === 'migrate') {
|
|
130
|
+
return umzug.up();
|
|
131
|
+
}
|
|
132
|
+
if (action === 'revert') {
|
|
133
|
+
return umzug.down({ to: 0 });
|
|
134
|
+
}
|
|
135
|
+
throw Error('No recognized action');
|
|
136
|
+
};
|
|
137
|
+
|
|
94
138
|
module.exports = {
|
|
95
139
|
createApp,
|
|
96
|
-
deleteApp,
|
|
97
140
|
createUserToken,
|
|
141
|
+
deleteApp,
|
|
98
142
|
listApps,
|
|
143
|
+
migrate,
|
|
99
144
|
resetIntegrationToken,
|
|
100
145
|
};
|