ti2 1.0.33 → 1.0.35
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 +70 -0
- package/controllers/__tests__/app.js +13 -1
- package/controllers/__tests__/user.js +10 -0
- package/controllers/app.js +63 -3
- package/index.js +1 -1
- package/package.json +1 -1
- package/test/plugin.js +19 -0
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']
|
|
@@ -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
|
-
|
|
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}`,
|
package/controllers/app.js
CHANGED
|
@@ -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: {
|
|
@@ -58,7 +82,8 @@ const createAppToken = async (req, res, next) => {
|
|
|
58
82
|
// create any cronjobs related to the app
|
|
59
83
|
await bb.each(req.app.plugins, async plugin => {
|
|
60
84
|
if (Array.isArray(plugin.jobs)) {
|
|
61
|
-
|
|
85
|
+
const validJobs = plugin.jobs.filter(job => Boolean(job.cron) && Boolean(job.method));
|
|
86
|
+
await bb.each(validJobs, async job => {
|
|
62
87
|
const where = {
|
|
63
88
|
pluginName: plugin.name,
|
|
64
89
|
pluginJobId: job.method,
|
|
@@ -140,6 +165,39 @@ const deleteAppToken = async (req, res, next) => {
|
|
|
140
165
|
return res.json({ message: `${retVal} erased` });
|
|
141
166
|
};
|
|
142
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
|
+
|
|
143
201
|
const listAppTokens = async (req, res, next) => {
|
|
144
202
|
const { params: { app: integrationId } } = req;
|
|
145
203
|
try {
|
|
@@ -252,7 +310,7 @@ const getJobStatus = async (req, res, next) => {
|
|
|
252
310
|
}
|
|
253
311
|
};
|
|
254
312
|
|
|
255
|
-
module.exports = {
|
|
313
|
+
module.exports = plugins => ({
|
|
256
314
|
createAppToken,
|
|
257
315
|
deleteAppToken,
|
|
258
316
|
getAppScheduledJobs,
|
|
@@ -261,4 +319,6 @@ module.exports = {
|
|
|
261
319
|
listAppTokens,
|
|
262
320
|
migrateApp,
|
|
263
321
|
runAppJob,
|
|
264
|
-
|
|
322
|
+
tokenTemplate,
|
|
323
|
+
validateAppToken: validateAppToken(plugins),
|
|
324
|
+
});
|
package/index.js
CHANGED
package/package.json
CHANGED
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
|