ti2 1.0.34 → 1.0.36

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/api.yml CHANGED
@@ -96,6 +96,14 @@ components:
96
96
  type: string
97
97
  description: An identifier for this token, it should NOT be the token itself but a short safe version of it
98
98
  example: 'expediaTestLocation-23'
99
+ UserAppValidate:
100
+ description: A user + App configuration to validate
101
+ type: object
102
+ properties:
103
+ tokenHint:
104
+ type: string
105
+ description: An identifier for this token, it should NOT be the token itself but a short safe version of it
106
+ example: 'expediaTestLocation-23'
99
107
  UserId:
100
108
  description: userId to create the token for
101
109
  type: object
@@ -372,6 +380,30 @@ paths:
372
380
  application/json:
373
381
  schema:
374
382
  $ref: '#/components/schemas/Message'
383
+ /app/{appKey}/tokenTemplate:
384
+ get:
385
+ security:
386
+ - bearerAuth: ['admin', 'user']
387
+ tags:
388
+ - admin
389
+ - user
390
+ summary: Should return the token template
391
+ operationId: tokenTemplate
392
+ parameters:
393
+ - name: appKey
394
+ in: path
395
+ description: Ann name to get the token template attributes
396
+ required: true
397
+ example: 'expedia'
398
+ schema:
399
+ type: string
400
+ responses:
401
+ 200:
402
+ description: success
403
+ content:
404
+ application/json:
405
+ schema:
406
+ $ref: '#/components/schemas/AnyValue'
375
407
  /apps:
376
408
  get:
377
409
  security:
@@ -563,6 +595,44 @@ paths:
563
595
  application/json:
564
596
  schema: '#/components/schemas/Message'
565
597
 
598
+ /{app}/{userId}/validate:
599
+ post:
600
+ tags:
601
+ - app
602
+ - user
603
+ summary: Should delete a user+appToken configuration, the auth token can be from an app or a user
604
+ security:
605
+ - bearerAuth: ['admin', 'app', 'user']
606
+ operationId: validateAppToken
607
+ parameters:
608
+ - name: app
609
+ in: path
610
+ description: app name to validate (must match app token)
611
+ example: 'expedia'
612
+ required: true
613
+ schema:
614
+ type: string
615
+ - name: userId
616
+ in: path
617
+ description: owner of the token to validate
618
+ example: '536830b6ed19afa44a000002'
619
+ required: true
620
+ schema:
621
+ type: string
622
+ requestBody:
623
+ description: The User+App configuration to validate
624
+ required: true
625
+ content:
626
+ application/json:
627
+ schema:
628
+ $ref: '#/components/schemas/UserAppValidate'
629
+ responses:
630
+ 200:
631
+ description: success
632
+ content:
633
+ application/json:
634
+ schema: '#/components/schemas/Message'
635
+
566
636
  /{app}/{userId}/jobRun:
567
637
  security:
568
638
  - bearerAuth: ['app', 'user', 'admin']
package/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { Command, Argument } = require('commander');
4
4
 
5
- const { migrateApp } = require('./controllers/app');
5
+ const { migrateApp } = require('./controllers/app')();
6
6
  const { migrate } = require('./controllers/admin');
7
7
 
8
8
  const program = new Command();
@@ -5,7 +5,7 @@ const R = require('ramda');
5
5
 
6
6
  const testUtils = require('../../test/utils');
7
7
  const slugify = require('../../test/slugify');
8
- const appController = require('../app');
8
+ let appController = require('../app');
9
9
 
10
10
  const { env: { adminKey, jwtSecret } } = process;
11
11
 
@@ -37,6 +37,7 @@ describe('app', () => {
37
37
  ({ doApiPost, doApiGet, doApiPut } = await testUtils({
38
38
  plugins: [appName],
39
39
  }));
40
+ appController = appController([appName]);
40
41
  });
41
42
  it('should create a new app', async () => {
42
43
  ({ value: appKey } = await doApiPost({
@@ -69,6 +70,17 @@ describe('app', () => {
69
70
  });
70
71
  expect(parseInt(value, 10)).toBeGreaterThan(0);
71
72
  });
73
+ it('should be able to test a user token for the app', async () => {
74
+ const { valid } = await doApiPost({
75
+ url: `/${appName}/${userId}/validate`,
76
+ token: appKey,
77
+ payload: {
78
+ tokenHint: apiKey.split('-')[0],
79
+ token,
80
+ },
81
+ });
82
+ expect(valid).toBe(true);
83
+ });
72
84
  describe('jobs', () => {
73
85
  it('the scheduled job should have been created for the user', async () => {
74
86
  const jobs = R.path(
@@ -1,5 +1,6 @@
1
1
  /* globals describe it expect beforeAll, jest, afterEach */
2
2
 
3
+ const R = require('ramda');
3
4
  const chance = require('chance').Chance();
4
5
  const testUtils = require('../../test/utils');
5
6
  const slugify = require('../../test/slugify');
@@ -76,6 +77,15 @@ describe('user', () => {
76
77
  ]),
77
78
  );
78
79
  });
80
+ it('should be able to get the app\'s token template', async () => {
81
+ const { template } = await doApiGet({
82
+ url: `/app/${appName}/tokenTemplate`,
83
+ token: userKey,
84
+ payload: {},
85
+ });
86
+ expect(template).toBeTruthy();
87
+ expect(R.path(['apiKey', 'regExp', 'source'], template)).toBeTruthy();
88
+ });
79
89
  it('should be able to delete a user/app key', async () => {
80
90
  await doApiDelete({
81
91
  url: `/${appName}/${userId}`,
@@ -26,6 +26,30 @@ const jwtEncode = async (req, res, next) => {
26
26
  }
27
27
  };
28
28
 
29
+ const tokenTemplate = async (req, res, next) => {
30
+ const {
31
+ params: {
32
+ appKey: pluginName,
33
+ },
34
+ } = req;
35
+ try {
36
+ const thePlugin = req.app.plugins.find(({ name }) => name === pluginName);
37
+ assert(thePlugin);
38
+ let template = thePlugin.tokenTemplate();
39
+ const safeRegExp = el => ({
40
+ ...el,
41
+ regExp: {
42
+ flags: el.regExp.flags,
43
+ source: el.regExp.source,
44
+ },
45
+ });
46
+ template = R.map(safeRegExp, template);
47
+ return res.json({ template });
48
+ } catch (err) {
49
+ return next(err);
50
+ }
51
+ };
52
+
29
53
  const createAppToken = async (req, res, next) => {
30
54
  const {
31
55
  body: {
@@ -141,6 +165,39 @@ const deleteAppToken = async (req, res, next) => {
141
165
  return res.json({ message: `${retVal} erased` });
142
166
  };
143
167
 
168
+ const validateAppToken = plugins => async (req, res, next) => {
169
+ const {
170
+ body: {
171
+ tokenHint: hint,
172
+ },
173
+ params: {
174
+ app: appKey,
175
+ userId,
176
+ },
177
+ } = req;
178
+ try {
179
+ const app = plugins.find(({ name }) => name === appKey);
180
+ assert(app, `could not find the app ${appKey}`);
181
+ const userAppKeys = await sqldb.UserAppKey.findOne({
182
+ where: {
183
+ userId,
184
+ integrationId: appKey,
185
+ ...(hint ? { hint } : {}),
186
+ },
187
+ });
188
+ assert(userAppKeys, 'could not find the app key');
189
+ const token = userAppKeys.appKey;
190
+ assert(app.validateToken, `could not find the validateToken method for ${appKey}`);
191
+ const valid = await app.validateToken({
192
+ token,
193
+ });
194
+ return res.json({ valid });
195
+ } catch (err) {
196
+ return next(err);
197
+ }
198
+ };
199
+
200
+
144
201
  const listAppTokens = async (req, res, next) => {
145
202
  const { params: { app: integrationId } } = req;
146
203
  try {
@@ -253,7 +310,7 @@ const getJobStatus = async (req, res, next) => {
253
310
  }
254
311
  };
255
312
 
256
- module.exports = {
313
+ module.exports = plugins => ({
257
314
  createAppToken,
258
315
  deleteAppToken,
259
316
  getAppScheduledJobs,
@@ -262,4 +319,6 @@ module.exports = {
262
319
  listAppTokens,
263
320
  migrateApp,
264
321
  runAppJob,
265
- };
322
+ tokenTemplate,
323
+ validateAppToken: validateAppToken(plugins),
324
+ });
package/index.js CHANGED
@@ -82,7 +82,7 @@ module.exports = async ({
82
82
  const api = {
83
83
  ...pingController,
84
84
  ...adminController,
85
- ...appController,
85
+ ...appController(plugins),
86
86
  ...userController(plugins),
87
87
  ...bookingsController(plugins),
88
88
  ...allotmentController(plugins),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ti2",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "description": "Tourist Industry Exchange (TI2)",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/test/plugin.js CHANGED
@@ -54,6 +54,13 @@ class Plugin {
54
54
  this.searchQuote = jestPlugin.fn(() => ({ quote: [{ id: chance.guid() }] }));
55
55
  this.createBooking = jestPlugin.fn(() => {});
56
56
  this.queryAllotment = jestPlugin.fn(() => ({ allotments: [] }));
57
+ this.tokenTemplate = jestPlugin.fn(() => ({
58
+ apiKey: {
59
+ type: 'text',
60
+ regExp: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/,
61
+ description: 'Api Key',
62
+ },
63
+ }));
57
64
 
58
65
  /**
59
66
  * Background and schedule Jos
@@ -81,6 +88,18 @@ class Plugin {
81
88
  validateToken() {
82
89
  return Boolean(this);
83
90
  }
91
+
92
+ /**
93
+ * Returns the the token template of a plugin.
94
+ * Properties are dynamic since every attribute should correspond to a token
95
+ * key, every attribute should contain at least the type (ie text) a regExp and
96
+ * a description.
97
+ * The regExp property gets decomposed into both source and flags as strings.
98
+ */
99
+ tokenTemplate() {
100
+ return {};
101
+ }
102
+
84
103
  /**
85
104
  * GPS Location Object
86
105
  * @typedef {Object} GPSLocation