ti2 1.0.30 → 1.0.33
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 +101 -0
- package/controllers/__tests__/allotments.js +106 -0
- package/controllers/__tests__/bookings.js +1 -1
- package/controllers/allotment.js +39 -0
- package/index.js +8 -3
- package/package.json +1 -1
- package/test/plugin.js +21 -1
package/api.yml
CHANGED
|
@@ -1342,3 +1342,104 @@ paths:
|
|
|
1342
1342
|
application/json:
|
|
1343
1343
|
schema:
|
|
1344
1344
|
$ref: '#/components/schemas/AnyValue'
|
|
1345
|
+
/allotment/{appKey}/{userId}:
|
|
1346
|
+
get:
|
|
1347
|
+
security:
|
|
1348
|
+
- bearerAuth: ['admin', 'user']
|
|
1349
|
+
tags:
|
|
1350
|
+
- allotment
|
|
1351
|
+
summary: Query the allotment
|
|
1352
|
+
operationId: queryAllotment
|
|
1353
|
+
parameters:
|
|
1354
|
+
- name: appKey
|
|
1355
|
+
in: path
|
|
1356
|
+
description: app to create the booking for, defaults to first token
|
|
1357
|
+
required: true
|
|
1358
|
+
example: 'tourplan'
|
|
1359
|
+
schema:
|
|
1360
|
+
type: string
|
|
1361
|
+
- name: userId
|
|
1362
|
+
in: path
|
|
1363
|
+
description: the user/company that the appKey belongs to
|
|
1364
|
+
required: true
|
|
1365
|
+
schema:
|
|
1366
|
+
type: string
|
|
1367
|
+
- in: query
|
|
1368
|
+
name: startDate
|
|
1369
|
+
schema:
|
|
1370
|
+
type: string
|
|
1371
|
+
description: the start date of the requested allotment (dd/mm/yyyy)
|
|
1372
|
+
default: "01/04/2023"
|
|
1373
|
+
- in: query
|
|
1374
|
+
name: endDate
|
|
1375
|
+
schema:
|
|
1376
|
+
type: string
|
|
1377
|
+
description: the end date of the requested allotment (dd/mm/yyyy)
|
|
1378
|
+
default: "03/04/2023"
|
|
1379
|
+
- in: query
|
|
1380
|
+
name: keyPath
|
|
1381
|
+
schema:
|
|
1382
|
+
type: string
|
|
1383
|
+
description: a string with the product path separater by pipes ie. supplierId|locationId|productID
|
|
1384
|
+
default: "MAGLUX|7CQLDACMAGLUXDELSUI"
|
|
1385
|
+
responses:
|
|
1386
|
+
'200':
|
|
1387
|
+
description: Allotment Details
|
|
1388
|
+
content:
|
|
1389
|
+
application/json:
|
|
1390
|
+
schema:
|
|
1391
|
+
$ref: '#/components/schemas/AnyValue'
|
|
1392
|
+
/allotment/{appKey}/{hint}/{userId}:
|
|
1393
|
+
get:
|
|
1394
|
+
security:
|
|
1395
|
+
- bearerAuth: ['admin', 'user']
|
|
1396
|
+
tags:
|
|
1397
|
+
- allotment
|
|
1398
|
+
summary: Query the allotment with an specific hit
|
|
1399
|
+
operationId: queryAllotment
|
|
1400
|
+
parameters:
|
|
1401
|
+
- name: appKey
|
|
1402
|
+
in: path
|
|
1403
|
+
description: app to create the booking for, defaults to first token
|
|
1404
|
+
required: true
|
|
1405
|
+
example: 'tourplan'
|
|
1406
|
+
schema:
|
|
1407
|
+
type: string
|
|
1408
|
+
- name: hint
|
|
1409
|
+
in: path
|
|
1410
|
+
description: a specific token hint to be used
|
|
1411
|
+
required: true
|
|
1412
|
+
example: 'tourplan-australia'
|
|
1413
|
+
schema:
|
|
1414
|
+
type: string
|
|
1415
|
+
- name: userId
|
|
1416
|
+
in: path
|
|
1417
|
+
description: the user/company that the appKey belongs to
|
|
1418
|
+
required: true
|
|
1419
|
+
schema:
|
|
1420
|
+
type: string
|
|
1421
|
+
- in: query
|
|
1422
|
+
name: startDate
|
|
1423
|
+
schema:
|
|
1424
|
+
type: string
|
|
1425
|
+
description: the start date of the requested allotment (dd/mm/yyyy)
|
|
1426
|
+
default: "01/04/2023"
|
|
1427
|
+
- in: query
|
|
1428
|
+
name: endDate
|
|
1429
|
+
schema:
|
|
1430
|
+
type: string
|
|
1431
|
+
description: the end date of the requested allotment (dd/mm/yyyy)
|
|
1432
|
+
default: "03/04/2023"
|
|
1433
|
+
- in: query
|
|
1434
|
+
name: keyPath
|
|
1435
|
+
schema:
|
|
1436
|
+
type: string
|
|
1437
|
+
description: a string with the product path separater by pipes ie. supplierId|locationId|productID
|
|
1438
|
+
default: "MAGLUX|7CQLDACMAGLUXDELSUI"
|
|
1439
|
+
responses:
|
|
1440
|
+
'200':
|
|
1441
|
+
description: Allotment Details
|
|
1442
|
+
content:
|
|
1443
|
+
application/json:
|
|
1444
|
+
schema:
|
|
1445
|
+
$ref: '#/components/schemas/AnyValue'
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* globals beforeAll describe it expect */
|
|
2
|
+
|
|
3
|
+
const chance = require('chance').Chance();
|
|
4
|
+
const testUtils = require('../../test/utils');
|
|
5
|
+
|
|
6
|
+
const { env: { adminKey } } = process;
|
|
7
|
+
|
|
8
|
+
describe('allotment', () => {
|
|
9
|
+
const newApp = {
|
|
10
|
+
name: 'tourplantest',
|
|
11
|
+
packageName: 'ti2-tourplanTest',
|
|
12
|
+
adminEmail: 'engineering+tourplantest@tourconnect.com',
|
|
13
|
+
};
|
|
14
|
+
const appKey = newApp.name;
|
|
15
|
+
const userId = '551394be5ac58e5c76000019';
|
|
16
|
+
const token = {
|
|
17
|
+
endpoint: 'https://someendpoint.com',
|
|
18
|
+
username: chance.guid(),
|
|
19
|
+
password: chance.string(),
|
|
20
|
+
};
|
|
21
|
+
const newIntegration = {
|
|
22
|
+
tokenHint: 'testingToken',
|
|
23
|
+
token,
|
|
24
|
+
};
|
|
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: [appKey],
|
|
37
|
+
}));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let userToken;
|
|
41
|
+
it('drop any existing user integration', async () => {
|
|
42
|
+
try {
|
|
43
|
+
await doApiDelete({
|
|
44
|
+
url: `/${appKey}/${userId}`,
|
|
45
|
+
token: adminKey,
|
|
46
|
+
payload: { tokenHint: newIntegration.tokenHint },
|
|
47
|
+
});
|
|
48
|
+
} catch (err) {
|
|
49
|
+
// console.debug(err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
it('create the user', async () => {
|
|
53
|
+
({ value: userToken } = await doApiPost({
|
|
54
|
+
url: '/user',
|
|
55
|
+
token: adminKey,
|
|
56
|
+
payload: { userId },
|
|
57
|
+
}));
|
|
58
|
+
expect(userToken).toBeTruthy();
|
|
59
|
+
});
|
|
60
|
+
it('create the App+User setup', async () => {
|
|
61
|
+
const { userAppKeys } = await doApiGet({
|
|
62
|
+
url: `/user/${userId}/apps`,
|
|
63
|
+
token: userToken,
|
|
64
|
+
});
|
|
65
|
+
if (!userAppKeys.map(e => e.integrationId).includes(appKey)) {
|
|
66
|
+
// relation does not exits
|
|
67
|
+
const { value } = await doApiPost({
|
|
68
|
+
url: `/${appKey}/${userId}`,
|
|
69
|
+
token: userToken,
|
|
70
|
+
payload: newIntegration,
|
|
71
|
+
});
|
|
72
|
+
expect(value).toBeTruthy();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const payload = {
|
|
76
|
+
dateFormat: 'DD-MM-YYYY',
|
|
77
|
+
startDate: '01-12-2022',
|
|
78
|
+
endDate: '15-12-2022',
|
|
79
|
+
keyPath: `${chance.guid()}|${chance.guid()}`,
|
|
80
|
+
};
|
|
81
|
+
const urlParams = new URLSearchParams(payload).toString();
|
|
82
|
+
it('should be able to get some allotments', async () => {
|
|
83
|
+
const { allotments } = await doApiGet({
|
|
84
|
+
url: `/allotment/${appKey}/${userId}?${urlParams}`,
|
|
85
|
+
token: userToken,
|
|
86
|
+
payload,
|
|
87
|
+
});
|
|
88
|
+
expect(plugins[0].queryAllotment).toHaveBeenCalled();
|
|
89
|
+
expect(Array.isArray(allotments)).toBeTruthy();
|
|
90
|
+
const call = plugins[0].queryAllotment.mock.calls[0][0];
|
|
91
|
+
expect(call.payload).toEqual(payload);
|
|
92
|
+
expect(call.token).toEqual(token);
|
|
93
|
+
});
|
|
94
|
+
it('should be able to get some allotments with a hint', async () => {
|
|
95
|
+
const { allotments } = await doApiGet({
|
|
96
|
+
url: `/allotment/${appKey}/${newIntegration.tokenHint}/${userId}?${urlParams}`,
|
|
97
|
+
token: userToken,
|
|
98
|
+
payload,
|
|
99
|
+
});
|
|
100
|
+
expect(plugins[0].queryAllotment).toHaveBeenCalled();
|
|
101
|
+
expect(Array.isArray(allotments)).toBeTruthy();
|
|
102
|
+
const call = plugins[0].queryAllotment.mock.calls[0][0];
|
|
103
|
+
expect(call.payload).toEqual(payload);
|
|
104
|
+
expect(call.token).toEqual(token);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -89,7 +89,7 @@ describe('user: booking search', () => {
|
|
|
89
89
|
});
|
|
90
90
|
it('should be able to search a for a booking', async () => {
|
|
91
91
|
const payload = {
|
|
92
|
-
bookingId: '',
|
|
92
|
+
bookingId: '', supplierBookingId: '', name: '',
|
|
93
93
|
};
|
|
94
94
|
const { bookings } = await doApiPost({
|
|
95
95
|
url: `/bookings/${appKey}/${userId}/search`,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
|
|
3
|
+
const { UserAppKey } = require('../models/index');
|
|
4
|
+
|
|
5
|
+
const queryAllotment = plugins => async (req, res, next) => {
|
|
6
|
+
const {
|
|
7
|
+
params: {
|
|
8
|
+
appKey,
|
|
9
|
+
hint,
|
|
10
|
+
userId,
|
|
11
|
+
},
|
|
12
|
+
query,
|
|
13
|
+
} = req;
|
|
14
|
+
try {
|
|
15
|
+
const app = plugins.find(({ name }) => name === appKey);
|
|
16
|
+
assert(app, `could not find the app ${appKey}`);
|
|
17
|
+
const userAppKeys = await UserAppKey.findOne({
|
|
18
|
+
where: {
|
|
19
|
+
userId,
|
|
20
|
+
integrationId: appKey,
|
|
21
|
+
...(hint ? { hint } : {}),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
assert(userAppKeys, 'could not find the app key');
|
|
25
|
+
const token = userAppKeys.appKey;
|
|
26
|
+
assert(app.queryAllotment, 'could not find the allotment method');
|
|
27
|
+
const results = await app.queryAllotment({
|
|
28
|
+
token,
|
|
29
|
+
payload: query,
|
|
30
|
+
});
|
|
31
|
+
return res.json(results);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
return next(err);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = plugins => ({
|
|
38
|
+
queryAllotment: queryAllotment(plugins),
|
|
39
|
+
});
|
package/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const adminController = require('./controllers/admin');
|
|
|
17
17
|
const appController = require('./controllers/app');
|
|
18
18
|
const userController = require('./controllers/user');
|
|
19
19
|
const bookingsController = require('./controllers/bookings');
|
|
20
|
+
const allotmentController = require('./controllers/allotment');
|
|
20
21
|
const { Integration } = require('./models');
|
|
21
22
|
const cache = require('./cache');
|
|
22
23
|
|
|
@@ -26,7 +27,6 @@ const rebuild = fn => obj =>
|
|
|
26
27
|
|
|
27
28
|
const isNumber = value => !Number.isNaN(Number(value));
|
|
28
29
|
|
|
29
|
-
|
|
30
30
|
module.exports = async ({
|
|
31
31
|
apiDocs = true,
|
|
32
32
|
elasticLogIndex = 'apilog_ti2',
|
|
@@ -67,7 +67,8 @@ module.exports = async ({
|
|
|
67
67
|
},
|
|
68
68
|
raw: true,
|
|
69
69
|
});
|
|
70
|
-
const
|
|
70
|
+
const matchedIntegrationsNames = matchedIntegrations.map(R.prop('name'));
|
|
71
|
+
const missingIntegrations = R.difference(pluginNames, matchedIntegrationsNames);
|
|
71
72
|
if (missingIntegrations.length > 0) {
|
|
72
73
|
// need to crete the missing integrations
|
|
73
74
|
await Integration.bulkCreate(missingIntegrations.map(name => ({
|
|
@@ -84,6 +85,7 @@ module.exports = async ({
|
|
|
84
85
|
...appController,
|
|
85
86
|
...userController(plugins),
|
|
86
87
|
...bookingsController(plugins),
|
|
88
|
+
...allotmentController(plugins),
|
|
87
89
|
cache: R.omit(['cache'], cache),
|
|
88
90
|
}; // mehthods that should map to the yaml api spec
|
|
89
91
|
app.plugins = plugins;
|
|
@@ -207,7 +209,10 @@ module.exports = async ({
|
|
|
207
209
|
connect(app);
|
|
208
210
|
app.use(middleware.mock());
|
|
209
211
|
// global error Handling
|
|
210
|
-
app.use((err, req, res) => {
|
|
212
|
+
app.use((err, req, res, next) => {
|
|
213
|
+
if (res.headersSent) {
|
|
214
|
+
return next(err);
|
|
215
|
+
}
|
|
211
216
|
if (process.env.CONSOLE_ERRORS || process.env.JEST_WORKER_ID) {
|
|
212
217
|
console.error(err);
|
|
213
218
|
}
|
package/package.json
CHANGED
package/test/plugin.js
CHANGED
|
@@ -53,6 +53,7 @@ class Plugin {
|
|
|
53
53
|
});
|
|
54
54
|
this.searchQuote = jestPlugin.fn(() => ({ quote: [{ id: chance.guid() }] }));
|
|
55
55
|
this.createBooking = jestPlugin.fn(() => {});
|
|
56
|
+
this.queryAllotment = jestPlugin.fn(() => ({ allotments: [] }));
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* Background and schedule Jos
|
|
@@ -292,7 +293,7 @@ class Plugin {
|
|
|
292
293
|
* @property {Cancelled|Active|Pending} status The currrent booking status.
|
|
293
294
|
* @property {Holder} holder Booking holder information.
|
|
294
295
|
* @property {string} telephone Contact telephone.
|
|
295
|
-
* @property {string}
|
|
296
|
+
* @property {string} supplierBookingId Booking Id for the supplier booking system
|
|
296
297
|
* @property {string} hotelId A HotelId if the booking is for an accommodation.
|
|
297
298
|
* @property {string} hotelName A Hotel Name if the booking is for an accommodation.
|
|
298
299
|
* @property {BookingRoom[]} rooms Booking Room details.
|
|
@@ -437,6 +438,25 @@ class Plugin {
|
|
|
437
438
|
* @returns {Booking} retVal.booking - A Booking object.
|
|
438
439
|
*/
|
|
439
440
|
createBooking() {}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Query Allotment
|
|
444
|
+
* @async
|
|
445
|
+
* @param {Object} args - Allotment query arguments.
|
|
446
|
+
* @param {Object} args.token - A token definition, it's content varies between integrations.
|
|
447
|
+
* @param {Object} args.payload - Search spect object.
|
|
448
|
+
* dateFormat = 'DD/MM/YYYY',
|
|
449
|
+
startDate,
|
|
450
|
+
endDate,
|
|
451
|
+
keyPath,
|
|
452
|
+
* @param {string} args.payload.dateFormat - Date format to use for params and return value (i.e. DD/MM/YYYY
|
|
453
|
+
* @param {string} args.payload.startDate - Start date for the query of allotment objects
|
|
454
|
+
* @param {string} args.payload.endDate - End date for the query of allotment objects
|
|
455
|
+
* @param {string} args.payload.keyPath - End date for the query of allotment objects
|
|
456
|
+
* @returns {object} retVal - the return object.
|
|
457
|
+
* @returns {Booking} retVal.booking - A Booking object.
|
|
458
|
+
*/
|
|
459
|
+
queryAllotment() {}
|
|
440
460
|
}
|
|
441
461
|
|
|
442
462
|
module.exports = Plugin;
|