ti2 1.0.7 → 1.0.8

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.
@@ -8,14 +8,13 @@ on:
8
8
 
9
9
  jobs:
10
10
  tag:
11
- if: ${{ github.event.workflow_run.conclusion == 'success' }}
11
+ if: github.event.workflow_run.conclusion == 'success' && github.repository == 'ti2travel/ti2'
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
14
  - uses: actions/checkout@master
15
15
  - uses: tool3/bump@master
16
16
  with:
17
17
  github_token: ${{ secrets.GITHUB_TOKEN }}
18
- unrelated: true
19
18
  user: 'Github Action Job'
20
19
  email: 'engineering@tourconnect.com'
21
20
  branch: main
@@ -5,13 +5,37 @@ on:
5
5
  push:
6
6
 
7
7
  jobs:
8
- build_and_test:
8
+ test:
9
+ timeout-minutes: 10
9
10
  runs-on: ubuntu-latest
11
+ container:
12
+ image: node:erbium
13
+ services:
14
+ mysql:
15
+ image: mysql
16
+ env:
17
+ MYSQL_DATABASE: ti2development
18
+ MYSQL_ALLOW_EMPTY_PASSWORD: true
19
+ MYSQL_ROOT_PASSWORD: root
20
+ ports:
21
+ - 3306:3306
22
+ env:
23
+ CI: true
24
+ DB_URL: 'mysql://root:root@mysql/ti2development'
25
+ dbCryptoKey: 'YmYzYzE5OWMyNDcwY2I0NzdkOTA3YjFlMDkxN2MxN2I='
26
+ jwtSecret: '9rifawckwztuKehocJOtGM6deTqICGWzIyc5CqgNyi'
27
+ frontendKey: 'lYjEJHzb7lbQ1hk3ypjX'
28
+ adminKey: 'IxEAYjHbbUYhsPjQXwX1'
10
29
  steps:
11
- - uses: actions/checkout@v2
12
- - uses: actions/setup-node@v1
13
- with:
14
- node-version: 12
15
- - run: npm ci
16
- - run: npm run build --if-present
17
- - run: npm test
30
+ - uses: actions/checkout@v2
31
+ - uses: actions/setup-node@v1
32
+ with:
33
+ node-version: 12
34
+ - name: install dependencies
35
+ run: npm i
36
+ - name: create/migrate the database
37
+ run: npx sequelize db:create && npx sequelize db:migrate
38
+ - name: seed the database
39
+ run: npx sequelize db:seed:all
40
+ - name: run the tests
41
+ run: npx jest -i --coverage --forceExit
package/README.md CHANGED
@@ -8,7 +8,7 @@ Tourism Industry Interchange
8
8
  Some environment variables are required for ti2 to start
9
9
 
10
10
  - DB_URL (mysql connection in url format)
11
- - dbCryptoKey (integration details encription key)
11
+ - dbCryptoKey (integration details encription key, should be a 32 chars random string base64 encoded)
12
12
  - adminKey (a key for admin related requests)
13
13
  - jwtKey (a key to encrypt user sessions request)
14
14
  - frontendKey (an additional optional key to validate the origin of the requests)
package/api.yml CHANGED
@@ -227,6 +227,30 @@ paths:
227
227
  application/json:
228
228
  schema:
229
229
  $ref: '#/components/schemas/Message'
230
+
231
+ /app/{appKey}:
232
+ delete:
233
+ security:
234
+ - bearerAuth: ['admin']
235
+ tags:
236
+ - admin
237
+ summary: Should delete an App, the name of the app should be url safe (no spaces)
238
+ operationId: deleteApp
239
+ parameters:
240
+ - name: appKey
241
+ in: path
242
+ description: App name to delete, should be urlsafe and have a correspondant npm package already in the required npm modules as ti2-appName
243
+ required: true
244
+ example: 'expedia'
245
+ schema:
246
+ type: string
247
+ responses:
248
+ 200:
249
+ description: success
250
+ content:
251
+ application/json:
252
+ schema:
253
+ $ref: '#/components/schemas/Message'
230
254
  /apps:
231
255
  get:
232
256
  security:
@@ -1,10 +1,8 @@
1
- /* globals describe it expect afterAll */
1
+ /* globals describe it expect */
2
2
 
3
3
  const chance = require('chance').Chance();
4
- const request = require('supertest');
5
- const app = require('../../index');
6
- const sqldb = require('../../models/db');
7
- const { slugify } = require('../../test/utils');
4
+ const testUtils = require('../../test/utils');
5
+ const slugify = require('../../test/slugify');
8
6
 
9
7
  const { env: { adminKey } } = process;
10
8
 
@@ -17,26 +15,24 @@ describe('admin', () => {
17
15
  packageName: `ti2-${appName}`,
18
16
  adminEmail: chance.email(),
19
17
  };
18
+ const { doApiPost, doApiGet } = testUtils({
19
+ plugins: [appName],
20
+ });
20
21
  let appKey;
21
22
  const userId = '536830b6ed19afa44a000002';
22
- afterAll(async () => {
23
- await sqldb.connectionManager.close();
24
- });
25
23
  it('should be able to create an app', async () => {
26
- const resp = await request(app)
27
- .post('/app')
28
- .set('Authorization', `Bearer ${adminKey}`)
29
- .send(newApp);
30
- expect(resp.statusCode).toBe(200);
31
- expect(resp.body.value).toBeTruthy();
32
- appKey = resp.body.value;
24
+ ({ value: appKey } = await doApiPost({
25
+ url: '/app',
26
+ token: adminKey,
27
+ payload: newApp,
28
+ }));
29
+ expect(appKey).toBeTruthy();
33
30
  });
34
31
  it('the created app should be on the list of apps', async () => {
35
- const resp = await request(app)
36
- .get('/apps')
37
- .set('Authorization', `Bearer ${adminKey}`);
38
- expect(resp.statusCode).toBe(200);
39
- const { body: { integrations } } = resp;
32
+ const { integrations } = await doApiGet({
33
+ url: '/apps',
34
+ token: adminKey,
35
+ });
40
36
  expect(integrations).toEqual(
41
37
  expect.arrayContaining([
42
38
  expect.objectContaining(newApp),
@@ -45,18 +41,19 @@ describe('admin', () => {
45
41
  });
46
42
  it('should be able to reset an app\'s key', async () => {
47
43
  const url = `/app/resetAppKey/${newApp.name}`;
48
- const resp = await request(app)
49
- .get(url)
50
- .set('Authorization', `Bearer ${adminKey}`);
51
- expect(resp.statusCode).toBe(200);
52
- const { body: { value } } = resp;
44
+ const { value } = await doApiGet({
45
+ url,
46
+ token: adminKey,
47
+ });
53
48
  expect(value).toBeTruthy();
54
49
  expect(value).not.toBe(appKey);
55
50
  });
56
- it.skip('should be able to create a new user authentication token', async () => {
57
- const resp = await request(app).post('/user')
58
- .set('Authorization', `Bearer ${adminKey}`)
59
- .send({ userId });
60
- expect(resp.statusCode).toBe(200);
51
+ it('should be able to create a new user authentication token', async () => {
52
+ const { value } = await doApiPost({
53
+ url: '/user',
54
+ token: adminKey,
55
+ payload: { userId },
56
+ });
57
+ expect(value).toBeTruthy();
61
58
  });
62
59
  });
@@ -1,24 +1,24 @@
1
- /* globals describe it expect beforeAll, afterAll */
1
+ /* globals describe it expect */
2
2
 
3
3
  const chance = require('chance').Chance();
4
- const request = require('supertest');
5
4
  const jwt = require('jwt-promise');
6
-
7
- const app = require('../../index');
8
- const sqldb = require('../../models/db');
9
- const { slugify } = require('../../test/utils');
5
+ const testUtils = require('../../test/utils');
6
+ const slugify = require('../../test/slugify');
10
7
 
11
8
  const { env: { adminKey, jwtSecret } } = process;
12
9
 
13
10
  describe('app', () => {
14
11
  const appName = slugify(
15
12
  chance.company(),
16
- ).toLowerCase();
13
+ );
17
14
  const newApp = {
18
15
  name: appName,
19
16
  packageName: `ti2-${appName}`,
20
17
  adminEmail: chance.email(),
21
18
  };
19
+ const { doApiPost, doApiGet, doApiPut } = testUtils({
20
+ plugins: [appName],
21
+ });
22
22
  let appKey;
23
23
  const userId = chance.guid();
24
24
  const apiKey = chance.guid();
@@ -31,47 +31,43 @@ describe('app', () => {
31
31
  payload: { lorem: chance.paragraph() },
32
32
  };
33
33
  let encodedKey;
34
- beforeAll(async () => {
35
- const resp = await request(app)
36
- .post('/app')
37
- .set('Authorization', `Bearer ${adminKey}`)
38
- .send(newApp);
39
- expect(resp.statusCode).toBe(200);
40
- expect(resp.body.value).toBeTruthy();
41
- appKey = resp.body.value;
42
- });
43
- afterAll(async () => {
44
- await sqldb.connectionManager.close();
34
+ it('should create a new app', async () => {
35
+ ({ value: appKey } = await doApiPost({
36
+ url: '/app',
37
+ token: adminKey,
38
+ payload: newApp,
39
+ }));
40
+ expect(appKey).toBeTruthy();
45
41
  });
46
42
  it('should encode an airbitrary object', async () => {
47
- const resp = await request(app)
48
- .put(`/app/encode/${appName}`)
49
- .set('Authorization', `Bearer ${appKey}`)
50
- .send({ payload: encodePayload });
51
- expect(resp.statusCode).toBe(200);
52
- expect(resp.body.value).toBeTruthy();
53
- encodedKey = resp.body.value;
43
+ ({ value: encodedKey } = await doApiPut({
44
+ url: `/app/encode/${appName}`,
45
+ token: appKey,
46
+ payload: encodePayload,
47
+ }));
48
+ expect(encodedKey).toBeTruthy();
54
49
  });
55
50
  it('the encoded key should be decodable', async () => {
56
51
  const decoded = await jwt.verify(encodedKey, `${appName}.${jwtSecret}`);
57
- expect(decoded.payload).toEqual(expect.objectContaining(encodePayload));
52
+ expect(decoded).toEqual(expect.objectContaining(encodePayload));
58
53
  });
59
54
  it('should be able to create a user token for the app', async () => {
60
- const resp = await request(app)
61
- .post(`/${appName}/${userId}`)
62
- .set('Authorization', `Bearer ${appKey}`)
63
- .send({
55
+ const { value } = await doApiPost({
56
+ url: `/${appName}/${userId}`,
57
+ token: appKey,
58
+ payload: {
64
59
  tokenHint: apiKey.split('-')[0],
65
60
  token,
66
- });
67
- expect(resp.statusCode).toBe(200);
61
+ },
62
+ });
63
+ expect(parseInt(value, 10)).toBeGreaterThan(0);
68
64
  });
69
65
  it('should be able to get a list of all tokens related to the app', async () => {
70
- const resp = await request(app)
71
- .get(`/app/tokens/${appName}`)
72
- .set('Authorization', `Bearer ${appKey}`);
73
- expect(resp.statusCode).toBe(200);
74
- expect(resp.body.userAppKeys).toEqual(
66
+ const { userAppKeys } = await doApiGet({
67
+ url: `/app/tokens/${appName}`,
68
+ token: appKey,
69
+ });
70
+ expect(userAppKeys).toEqual(
75
71
  expect.arrayContaining([
76
72
  expect.objectContaining({
77
73
  hint: apiKey.split('-')[0],
@@ -1,43 +1,59 @@
1
1
  /* globals describe it expect */
2
2
 
3
- const { doApiGet, doApiPost } = require('../../test/utils');
3
+ const chance = require('chance').Chance();
4
+ const testUtils = require('../../test/utils');
4
5
 
5
6
  const { env: { adminKey } } = process;
6
7
 
7
- const newApp = {
8
- name: 'travelgate',
9
- packageName: 'ti2-travelgate',
10
- adminEmail: 'engineering+travelgate@tourconnect.com',
11
- };
12
- const appKey = newApp.name;
13
- const userId = '551394be5ac58e5c76000019';
14
- const newIntegration = {
15
- tokenHint: 'testingToken',
16
- token: {
8
+ describe('user: booking search', () => {
9
+ const newApp = {
10
+ name: 'travelgate',
11
+ packageName: 'ti2-travelgate',
12
+ adminEmail: 'engineering+travelgate@tourconnect.com',
13
+ };
14
+ let appKey = newApp.name;
15
+ const userId = '551394be5ac58e5c76000019';
16
+ const token = {
17
17
  endpoint: 'https://api.travelgatex.com',
18
- apiKey: '8ca687f8-7968-4331-7b1a-dfcb276e5e44',
18
+ apiKey: chance.guid(),
19
19
  client: 'tourconnect',
20
- },
21
- };
20
+ };
21
+ const newIntegration = {
22
+ tokenHint: 'testingToken',
23
+ token,
24
+ };
25
+ const {
26
+ doApiDelete,
27
+ doApiGet,
28
+ doApiPost,
29
+ plugins,
30
+ } = testUtils({
31
+ plugins: [newApp.name],
32
+ });
22
33
 
23
- describe('user: booking search', () => {
24
34
  let userToken;
35
+ it('drop any existing travelgateapp integration', async () => {
36
+ try {
37
+ await doApiDelete({
38
+ url: `/travelgate/${userId}`,
39
+ token: adminKey,
40
+ payload: { tokenHint: 'testingToken' },
41
+ });
42
+ } catch {}
43
+ try {
44
+ await doApiDelete({
45
+ url: '/app/travelgate',
46
+ token: adminKey,
47
+ });
48
+ } catch {}
49
+ });
25
50
  it('create the travelgate app', async () => {
26
- // create the travelgate app
27
- // get the list of existing
28
- const { integrations } = await doApiGet({
29
- url: '/apps',
51
+ const { value: apiKey } = await doApiPost({
52
+ url: '/app',
30
53
  token: adminKey,
54
+ payload: newApp,
31
55
  });
32
- if (!integrations.map(e => e.name).includes(newApp.name)) {
33
- // creating the app
34
- const { value: appKey } = await doApiPost({
35
- url: '/app',
36
- token: adminKey,
37
- payload: newApp,
38
- });
39
- expect(appKey).toBeTruthy();
40
- }
56
+ expect(apiKey).toBeTruthy();
41
57
  // create the user token
42
58
  ({ value: userToken } = await doApiPost({
43
59
  url: '/user',
@@ -62,14 +78,20 @@ describe('user: booking search', () => {
62
78
  }
63
79
  });
64
80
  it('should be able to search a travel gate for a booking', async () => {
65
- const retVal = await doApiPost({
81
+ const payload = {
82
+ bookingId: '', supplierId: '', name: '',
83
+ };
84
+ const { bookings } = await doApiPost({
66
85
  url: `/bookings/${appKey}/${userId}/search`,
67
86
  token: userToken,
68
- payload: {
69
- bookingId: '', supplierId: '', name: '',
70
- },
87
+ payload,
71
88
  });
72
- expect(Array.isArray(retVal.bookings)).toBeTruthy();
73
- expect(retVal.bookings.length > 0).toBeTruthy();
89
+ expect(plugins[0].searchHotelBooking).toHaveBeenCalled();
90
+ expect(Array.isArray(bookings)).toBeTruthy();
91
+ expect(plugins[0].searchHotelBooking.mock.calls[0][0].payload).toEqual(payload);
92
+ expect(plugins[0].searchHotelBooking.mock.calls[0][0].token).toEqual(token);
93
+
94
+ // expect(Array.isArray(retVal.bookings)).toBeTruthy();
95
+ // expect(retVal.bookings.length > 0).toBeTruthy();
74
96
  });
75
97
  });
@@ -1,18 +1,13 @@
1
1
  /* global describe, it, expect */
2
2
 
3
- // const should = require('should');
4
- const request = require('supertest');
5
- const server = require('../../index');
3
+ const { doApiGet } = require('../../test/utils')();
6
4
 
7
5
  describe('ping', () => {
8
6
  it('should get a server pong', async () => {
9
- const response = await request(server)
10
- .get('/ping')
11
- .set('Accept', 'application/json');
12
- expect(response.headers['content-type']).toMatch(/json/);
13
- expect(response.statusCode).toBe(200);
14
- expect(response.body.error).toBeUndefined();
15
- expect(Object.keys(response.body))
7
+ const response = await doApiGet({
8
+ url: '/ping',
9
+ });
10
+ expect(Object.keys(response))
16
11
  .toEqual(expect.arrayContaining([
17
12
  'name',
18
13
  'description',
@@ -1,10 +1,8 @@
1
- /* globals describe it expect beforeAll, afterAll, jest, afterEach */
1
+ /* globals describe it expect beforeAll, jest, afterEach */
2
2
 
3
3
  const chance = require('chance').Chance();
4
- const request = require('supertest');
5
- const { getRequireMocks, slugify } = require('../../test/utils');
6
- const app = require('../../index');
7
- const sqldb = require('../../models/db');
4
+ const testUtils = require('../../test/utils');
5
+ const slugify = require('../../test/slugify');
8
6
 
9
7
  const { env: { adminKey } } = process;
10
8
 
@@ -17,6 +15,9 @@ describe('user', () => {
17
15
  packageName: `ti2-${appName}`,
18
16
  adminEmail: chance.email(),
19
17
  };
18
+ const { doApiPost, doApiGet, doApiDelete } = testUtils({
19
+ plugins: [appName],
20
+ });
20
21
  let appKey;
21
22
  const userId = chance.guid();
22
23
  let apiKey = chance.guid();
@@ -27,43 +28,39 @@ describe('user', () => {
27
28
  };
28
29
  beforeAll(async () => {
29
30
  // create an App
30
- const resp = await request(app)
31
- .post('/app')
32
- .set('Authorization', `Bearer ${adminKey}`)
33
- .send(newApp);
34
- expect(resp.statusCode).toBe(200);
35
- expect(resp.body.value).toBeTruthy();
36
- appKey = resp.body.value;
37
- // create an App+User Mapping
38
- await request(app)
39
- .post(`/${appName}/${userId}`)
40
- .set('Authorization', `Bearer ${appKey}`)
41
- .send({
31
+ ({ value: appKey } = await doApiPost({
32
+ url: '/app',
33
+ token: adminKey,
34
+ payload: newApp,
35
+ }));
36
+ const url = `/${appName}/${userId}`;
37
+ await doApiPost({
38
+ url,
39
+ token: appKey,
40
+ payload: {
42
41
  tokenHint: apiKey.split('-')[0],
43
42
  token,
44
- });
43
+ },
44
+ });
45
45
  });
46
46
  let userKey;
47
- afterAll(async () => {
48
- await sqldb.connectionManager.close();
49
- });
50
47
  afterEach(() => jest.clearAllMocks());
51
48
  it('a user should be able to get a user token via an admin key', async () => {
52
- const resp = await request(app)
53
- .post('/user')
54
- .set('Authorization', `Bearer ${adminKey}`)
55
- .send({
56
- value: userId,
57
- });
58
- expect(resp.body.value).toBeTruthy();
59
- userKey = resp.body.value;
49
+ ({ value: userKey } = await doApiPost({
50
+ url: '/user',
51
+ token: adminKey,
52
+ payload: {
53
+ userId,
54
+ },
55
+ }));
56
+ expect(userKey).toBeTruthy();
60
57
  });
61
58
  it('a user should be able to get a list of it\'s mapped apps', async () => {
62
- const resp = await request(app)
63
- .get(`/user/${userId}/apps`)
64
- .set('Authorization', `Bearer ${userKey}`);
65
- expect(resp.statusCode).toBe(200);
66
- expect(resp.body.userAppKeys).toEqual(
59
+ const { userAppKeys } = await doApiGet({
60
+ token: userKey,
61
+ url: `/user/${userId}/apps`,
62
+ });
63
+ expect(userAppKeys).toEqual(
67
64
  expect.arrayContaining([
68
65
  expect.objectContaining({
69
66
  hint: apiKey.split('-')[0],
@@ -74,17 +71,16 @@ describe('user', () => {
74
71
  );
75
72
  });
76
73
  it('should be able to delete a user/app key', async () => {
77
- const resp = await request(app)
78
- .delete(`/${appName}/${userId}`)
79
- .set('Authorization', `Bearer ${userKey}`)
80
- .send({ tokenHint: apiKey.split('-')[0] });
81
- expect(resp.statusCode).toBe(200);
82
- // make sure the app token is NOT there
83
- const respList = await request(app)
84
- .get(`/user/${userId}/apps`)
85
- .set('Authorization', `Bearer ${userKey}`);
86
- expect(respList.statusCode).toBe(200);
87
- expect(respList.body.userAppKeys).toEqual(
74
+ await doApiDelete({
75
+ url: `/${appName}/${userId}`,
76
+ token: userKey,
77
+ payload: { tokenHint: apiKey.split('-')[0] },
78
+ });
79
+ const { userAppKeys } = await doApiGet({
80
+ url: `/user/${userId}/apps`,
81
+ token: userKey,
82
+ });
83
+ expect(userAppKeys).toEqual(
88
84
  expect.not.arrayContaining([
89
85
  expect.objectContaining({
90
86
  hint: apiKey.split('-')[0],
@@ -98,20 +94,20 @@ describe('user', () => {
98
94
  // set up new token
99
95
  apiKey = chance.guid();
100
96
  token.apiKey = apiKey;
101
- const resp = await request(app)
102
- .post(`/${appName}/${userId}`)
103
- .set('Authorization', `Bearer ${userKey}`)
104
- .send({
97
+ await doApiPost({
98
+ url: `/${appName}/${userId}`,
99
+ token: userKey,
100
+ payload: {
105
101
  tokenHint: apiKey.split('-')[0],
106
102
  token,
107
- });
108
- expect(resp.statusCode).toBe(200);
103
+ },
104
+ });
109
105
  // make sure the app token is there
110
- const respList = await request(app)
111
- .get(`/user/${userId}/apps`)
112
- .set('Authorization', `Bearer ${userKey}`);
113
- expect(respList.statusCode).toBe(200);
114
- expect(respList.body.userAppKeys).toEqual(
106
+ const { userAppKeys } = await doApiGet({
107
+ url: `/user/${userId}/apps`,
108
+ token: userKey,
109
+ });
110
+ expect(userAppKeys).toEqual(
115
111
  expect.arrayContaining([
116
112
  expect.objectContaining({
117
113
  hint: apiKey.split('-')[0],
@@ -122,12 +118,11 @@ describe('user', () => {
122
118
  );
123
119
  });
124
120
  it('should be able to get all the methods for an app', async () => {
125
- getRequireMocks({ jest, app: newApp });
126
- const methods = await request(app)
127
- .get(`/app/${appName}/methods`)
128
- .set('Authorization', `Bearer ${userKey}`);
129
- expect(methods.statusCode).toBe(200);
130
- expect(methods.body.methods).toEqual(
121
+ const { methods } = await doApiGet({
122
+ url: `/app/${appName}/methods`,
123
+ token: userKey,
124
+ });
125
+ expect(methods).toEqual(
131
126
  expect.arrayContaining([
132
127
  'validateToken', 'getProduct',
133
128
  ]),
@@ -12,21 +12,43 @@ const createApp = async (req, res, next) => {
12
12
  adminEmail,
13
13
  } = req.body;
14
14
  try {
15
- const newApp = await sqldb.Integration.create({
16
- name,
17
- packageName,
18
- adminEmail,
15
+ const existing = await sqldb.Integration.findByPk(name, { paranoid: false });
16
+ const apiKey = await (async () => {
17
+ if (existing) {
18
+ await existing.restore();
19
+ return existing.apiKey;
20
+ }
21
+ const newApp = await sqldb.Integration.create({
22
+ name,
23
+ packageName,
24
+ adminEmail,
25
+ });
26
+ return newApp.apiKey;
27
+ })();
28
+ return res.json({ value: apiKey });
29
+ } catch (err) {
30
+ return next(err);
31
+ }
32
+ };
33
+
34
+ const deleteApp = async (req, res, next) => {
35
+ const { pathParams: { appKey: name } } = req;
36
+ try {
37
+ const newApp = await sqldb.Integration.destroy({
38
+ where: { name },
19
39
  });
20
40
  const { apiKey } = newApp;
21
41
  return res.json({ value: apiKey });
22
42
  } catch (err) {
43
+ console.log({ err });
23
44
  return next(err);
24
45
  }
25
46
  };
26
47
 
27
48
  const listApps = async (req, res, next) => {
28
49
  try {
29
- const integrations = await sqldb.Integration.findAll().map(int => omit(['apiKey'], int.dataValues));
50
+ const integrations = (await sqldb.Integration.findAll())
51
+ .map(int => omit(['apiKey'], int.dataValues));
30
52
  return res.json({ integrations });
31
53
  } catch (err) {
32
54
  return next(err);
@@ -71,6 +93,7 @@ const resetIntegrationToken = async (req, res, next) => {
71
93
 
72
94
  module.exports = {
73
95
  createApp,
96
+ deleteApp,
74
97
  createUserToken,
75
98
  listApps,
76
99
  resetIntegrationToken,
@@ -22,9 +22,9 @@ const getAppMethods = plugins => async (req, res, next) => {
22
22
  const { params: { appKey } } = req;
23
23
  try {
24
24
  const app = plugins.filter(({ name }) => name === appKey)[0];
25
- // load(appKey);
25
+ const methods = Object.getOwnPropertyNames(app);
26
26
  return res.json({
27
- methods: Object.getOwnPropertyNames(Object.getPrototypeOf(app)),
27
+ methods,
28
28
  });
29
29
  } catch (err) {
30
30
  return next(err);
package/index.js CHANGED
@@ -39,6 +39,7 @@ module.exports = ({
39
39
  });
40
40
  return new Plugin({ name: pluginName, ...params });
41
41
  });
42
+ // console.log({ plugins });
42
43
  const api = {
43
44
  ...pingController,
44
45
  ...adminController,
@@ -46,6 +47,7 @@ module.exports = ({
46
47
  ...userController(plugins),
47
48
  ...bookingsController(plugins),
48
49
  }; // mehthods that should map to the yaml api spec
50
+ app.plugins = plugins;
49
51
  createMiddleware(schema, app, (_err, middleware) => {
50
52
  if (apiDocs) {
51
53
  app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(schema));
@@ -170,4 +172,4 @@ module.exports = ({
170
172
  });
171
173
 
172
174
  return app;
173
- }
175
+ };
@@ -1,6 +1,3 @@
1
- const assert = require('assert');
2
- const faker = require('faker');
3
-
4
1
  module.exports = {
5
2
  up: async queryInterface => {
6
3
  const payload = {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "ti2",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Tourist Industry Exchange (TI2)",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "jest || echo \"always ok\"",
7
+ "test": "jest",
8
8
  "sequelize": "sequelize-cli"
9
9
  },
10
10
  "repository": {
File without changes
package/test/plugin.js ADDED
@@ -0,0 +1,17 @@
1
+ /* global jest */
2
+
3
+ class Plugin {
4
+ constructor(params = {}) {
5
+ Object.entries(params).forEach(([attr, value]) => {
6
+ this[attr] = value;
7
+ });
8
+ }
9
+
10
+ validateToken = jest.fn();
11
+
12
+ getProduct = jest.fn();
13
+
14
+ searchHotelBooking = jest.fn(() => ({ bookings: [] }));
15
+ }
16
+
17
+ module.exports = Plugin;
@@ -0,0 +1,21 @@
1
+ // source : https://gist.github.com/codeguy/6684588
2
+
3
+ const slugify = strParam => {
4
+ let str = strParam.replace(/^\s+|\s+$/g, ''); // trim
5
+ str = str.toLowerCase();
6
+
7
+ // remove accents, swap ñ for n, etc
8
+ const from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;';
9
+ const to = 'aaaaeeeeiiiioooouuuunc------';
10
+ for (let i = 0, l = from.length; i < l; i += 1) {
11
+ str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
12
+ }
13
+
14
+ str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
15
+ .replace(/\s+/g, '-') // collapse whitespace and replace by -
16
+ .replace(/-+/g, '-'); // collapse dashes
17
+
18
+ return str;
19
+ };
20
+
21
+ module.exports = slugify;
package/test/utils.js CHANGED
@@ -1,111 +1,110 @@
1
+ /* global afterAll */
2
+
1
3
  const chance = require('chance').Chance();
2
4
  const request = require('supertest');
3
5
  const assert = require('assert');
4
-
5
- const app = require('../index');
6
- const pluginMock = require('../controllers/__mocks__/plugin');
6
+ const appReq = require('../index');
7
+ const Plugin = require('./plugin');
8
+ const slugify = require('./slugify');
9
+ const sqldb = require('../models/db');
7
10
 
8
11
  const { env: { adminKey } } = process;
9
12
 
10
13
  const timeout = ms => (new Promise(resolve => setTimeout(resolve, ms)));
11
14
 
12
- const getRequireMocks = ({ jest, app: { name } }) => {
13
- // create the mock dependencies
14
- const mock = jest.mock(`ti2-${name}`, () => ({
15
- ...pluginMock(),
16
- }), { virtual: true });
17
- return require(`ti2-${name}`);
18
- };
19
-
20
- // source : https://gist.github.com/codeguy/6684588
21
- const slugify = strParam => {
22
- let str = strParam.replace(/^\s+|\s+$/g, ''); // trim
23
- str = str.toLowerCase();
24
-
25
- // remove accents, swap ñ for n, etc
26
- const from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;';
27
- const to = 'aaaaeeeeiiiioooouuuunc------';
28
- for (let i = 0, l = from.length; i < l; i += 1) {
29
- str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
30
- }
15
+ afterAll(async () => {
16
+ await sqldb.connectionManager.close();
17
+ });
18
+ module.exports = (appParams = {}) => {
19
+ const plugins = (() => {
20
+ if (Array.isArray(appParams.plugins)) {
21
+ const retVal = {};
22
+ appParams.plugins.forEach(pluginName => {
23
+ retVal[pluginName] = Plugin;
24
+ });
25
+ return retVal;
26
+ }
27
+ return {
28
+ mockName: Plugin,
29
+ };
30
+ })();
31
+ const app = appReq({
32
+ startServer: false,
33
+ ...appParams,
34
+ plugins,
35
+ });
31
36
 
32
- str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
33
- .replace(/\s+/g, '-') // collapse whitespace and replace by -
34
- .replace(/-+/g, '-'); // collapse dashes
35
-
36
- return str;
37
- };
38
-
39
- const appSetup = async ({ userId: userIdParam } = {}) => {
40
- const userId = userIdParam || chance.guid();
41
- const appName1 = slugify(
42
- chance.company(),
43
- ).toLowerCase();
44
- const newApp = {
45
- name: appName1,
46
- packageName: `ti2-${appName1}`,
47
- adminEmail: chance.email(),
48
- };
49
- const apiKey1 = chance.guid();
50
- const token = {
51
- endpoint: chance.url(),
52
- apiKey: apiKey1,
53
- };
54
- const { body: { value: appKey } } = await request(app)
55
- .post('/app')
56
- .set('Authorization', `Bearer ${adminKey}`)
57
- .send(newApp);
58
- await request(app)
59
- .post(`/${newApp.name}/${userId}`)
60
- .set('Authorization', `Bearer ${appKey}`)
61
- .send({
62
- tokenHint: token.apiKey.split('-')[0],
63
- token,
64
- });
65
- return {
66
- newApp,
37
+ const doApi = async ({
38
+ query,
39
+ verb,
40
+ payload,
41
+ url,
67
42
  token,
68
- appKey,
69
- userId,
43
+ expectStatusCode = 200,
44
+ rawResponse = false,
45
+ }) => {
46
+ let resp;
47
+ if (token) {
48
+ resp = await request(app)[verb](url)
49
+ .set({ Authorization: `Bearer ${token}` })
50
+ .query(query)
51
+ .send(payload);
52
+ } else {
53
+ resp = await request(app)[verb](url).query(query).send(payload);
54
+ }
55
+ // if (resp.statusCode !== 200) assert.strictEqual(resp, `${url}-${expectStatusCode}`);
56
+ assert.strictEqual(`${url}-${resp.statusCode}`, `${url}-${expectStatusCode}`);
57
+ if (rawResponse) return resp;
58
+ return resp.body;
70
59
  };
71
- };
72
60
 
73
- const doApi = async ({
74
- query,
75
- verb,
76
- payload,
77
- url,
78
- token,
79
- expectStatusCode = 200,
80
- rawResponse = false,
81
- }) => {
82
- let resp;
83
- if (token) {
84
- resp = await request(app)[verb](url)
85
- .set({ Authorization: `Bearer ${token}` })
86
- .query(query)
87
- .send(payload);
88
- } else {
89
- resp = await request(app)[verb](url).query(query).send(payload);
90
- }
91
- // if (resp.statusCode !== 200) assert.strictEqual(resp, `${url}-${expectStatusCode}`);
92
- assert.strictEqual(`${url}-${resp.statusCode}`, `${url}-${expectStatusCode}`);
93
- if (rawResponse) return resp;
94
- return resp.body;
95
- };
61
+ const doApiGet = params => doApi({ ...params, verb: 'get' });
62
+ const doApiPost = params => doApi({ ...params, verb: 'post' });
63
+ const doApiPut = params => doApi({ ...params, verb: 'put' });
64
+ const doApiDelete = params => doApi({ ...params, verb: 'delete' });
96
65
 
97
- const doApiGet = params => doApi({ ...params, verb: 'get' });
98
- const doApiPost = params => doApi({ ...params, verb: 'post' });
99
- const doApiPut = params => doApi({ ...params, verb: 'put' });
100
- const doApiDelete = params => doApi({ ...params, verb: 'delete' });
66
+ const appSetup = async ({ userId: userIdParam } = {}) => {
67
+ const userId = userIdParam || chance.guid();
68
+ const appName1 = slugify(
69
+ chance.company(),
70
+ ).toLowerCase();
71
+ const newApp = {
72
+ name: appName1,
73
+ packageName: `ti2-${appName1}`,
74
+ adminEmail: chance.email(),
75
+ };
76
+ const apiKey1 = chance.guid();
77
+ const token = {
78
+ endpoint: chance.url(),
79
+ apiKey: apiKey1,
80
+ };
81
+ const { body: { value: appKey } } = await request(app)
82
+ .post('/app')
83
+ .set('Authorization', `Bearer ${adminKey}`)
84
+ .send(newApp);
85
+ await request(app)
86
+ .post(`/${newApp.name}/${userId}`)
87
+ .set('Authorization', `Bearer ${appKey}`)
88
+ .send({
89
+ tokenHint: token.apiKey.split('-')[0],
90
+ token,
91
+ });
92
+ return {
93
+ newApp,
94
+ token,
95
+ appKey,
96
+ userId,
97
+ };
98
+ };
101
99
 
102
- module.exports = {
103
- appSetup,
104
- doApiDelete,
105
- doApiGet,
106
- doApiPost,
107
- doApiPut,
108
- getRequireMocks,
109
- slugify,
110
- timeout,
100
+ return {
101
+ appSetup,
102
+ doApiDelete,
103
+ doApiGet,
104
+ doApiPost,
105
+ doApiPut,
106
+ slugify,
107
+ timeout,
108
+ plugins: app.plugins,
109
+ };
111
110
  };