ti2-tourplan 1.0.18 → 1.0.20
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/index.js +78 -28
- package/package.json +5 -2
- package/resolvers/product.js +18 -18
package/index.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
const Promise = require('bluebird');
|
|
1
|
+
const axiosRaw = require('axios');
|
|
2
|
+
// const Promise = require('bluebird');
|
|
3
3
|
const R = require('ramda');
|
|
4
4
|
const assert = require('assert');
|
|
5
5
|
const moment = require('moment');
|
|
6
6
|
const js2xmlparser = require('js2xmlparser');
|
|
7
7
|
const xml2js = require('xml2js');
|
|
8
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
8
9
|
const { translateTPOption } = require('./resolvers/product');
|
|
9
10
|
|
|
10
11
|
const Normalizer = require('./normalizer');
|
|
11
12
|
|
|
12
13
|
const xmlParser = new xml2js.Parser();
|
|
14
|
+
const fastParser = new XMLParser();
|
|
13
15
|
|
|
14
16
|
const defaultXmlOptions = {
|
|
15
17
|
prettyPrinting: { enabled: false },
|
|
@@ -25,7 +27,6 @@ const hostConnectXmlOptions = {
|
|
|
25
27
|
name: 'hostConnect_4_06_009.dtd',
|
|
26
28
|
},
|
|
27
29
|
};
|
|
28
|
-
|
|
29
30
|
const getHeaders = ({ length }) => ({
|
|
30
31
|
Accept: 'application/xml',
|
|
31
32
|
'Content-Type': 'application/xml; charset=utf-8',
|
|
@@ -82,16 +83,59 @@ class Plugin {
|
|
|
82
83
|
js2xmlparser.parse('Request', model, xmlOptions),
|
|
83
84
|
);
|
|
84
85
|
data = data.replace(xmlOptions.dtd.name, `Request SYSTEM "${xmlOptions.dtd.name}"`);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
let replyObj;
|
|
87
|
+
let errorStr;
|
|
88
|
+
// can't use proxy because of the static IP thing, darn
|
|
89
|
+
// if (this.xmlProxyUrl) {
|
|
90
|
+
// // use pyfilematch xmlproxy
|
|
91
|
+
// try {
|
|
92
|
+
// replyObj = R.path(['data'], await axios({
|
|
93
|
+
// method: 'post',
|
|
94
|
+
// url: `${this.xmlProxyUrl}/xmlproxy`,
|
|
95
|
+
// data: {
|
|
96
|
+
// url: endpoint,
|
|
97
|
+
// data,
|
|
98
|
+
// headers: getHeaders({ length: `${data.length}` }),
|
|
99
|
+
// },
|
|
100
|
+
// }));
|
|
101
|
+
// } catch (err) {
|
|
102
|
+
// console.log('error in calling pyfilematch xmlproxy', err);
|
|
103
|
+
// errorStr = `error in calling pyfilematch xmlproxy: ${err}`;
|
|
104
|
+
// }
|
|
105
|
+
// }
|
|
106
|
+
if (!replyObj) {
|
|
107
|
+
// in case of error /xmlproxy, fallback to call tourplan directly
|
|
108
|
+
// and then use pyfilematch xml2json to parse the xml
|
|
109
|
+
const reply = R.path(['data'], await axios({
|
|
110
|
+
method: 'post',
|
|
111
|
+
url: endpoint,
|
|
112
|
+
data,
|
|
113
|
+
headers: getHeaders({ length: data.length }),
|
|
114
|
+
}));
|
|
115
|
+
if (this.xmlProxyUrl) {
|
|
116
|
+
try {
|
|
117
|
+
// using raw axios to avoid logging the large xml request
|
|
118
|
+
({ data: replyObj } = await axiosRaw({
|
|
119
|
+
method: 'post',
|
|
120
|
+
url: `${this.xmlProxyUrl}/xml2json`,
|
|
121
|
+
data: { xml: reply },
|
|
122
|
+
maxContentLength: Infinity,
|
|
123
|
+
maxBodyLength: Infinity,
|
|
124
|
+
}));
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.log('error in calling pyfilematch xml2json', R.pathOr('Nada', ['response', 'data', 'error'], err));
|
|
127
|
+
errorStr = `error in calling pyfilematch xml2json: ${R.pathOr('Nada', ['response', 'data', 'error'], err)}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// in case of error from /xml2json, fallback to fast-xml-parser
|
|
131
|
+
if (!replyObj) {
|
|
132
|
+
replyObj = fastParser.parse(reply);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const requestType = R.keys(model)[0];
|
|
136
|
+
if (!replyObj) throw new Error(`${requestType} failed: ${errorStr || 'no reply object'}`);
|
|
137
|
+
const error = replyObj.error || R.path(['Reply', 'ErrorReply', 'Error'], replyObj);
|
|
93
138
|
if (error) {
|
|
94
|
-
const requestType = R.keys(model)[0];
|
|
95
139
|
if (error.indexOf('2050 SCN Request denied for TEST connecting from') > -1
|
|
96
140
|
&& requestType === 'OptionInfoRequest'
|
|
97
141
|
&& endpoint.indexOf('actour') > -1
|
|
@@ -130,7 +174,7 @@ class Plugin {
|
|
|
130
174
|
axios,
|
|
131
175
|
xmlOptions: hostConnectXmlOptions,
|
|
132
176
|
});
|
|
133
|
-
assert(R.path(['AgentInfoReply',
|
|
177
|
+
assert(R.path(['AgentInfoReply', 'Currency'], replyObj));
|
|
134
178
|
return true;
|
|
135
179
|
}
|
|
136
180
|
const model = {
|
|
@@ -142,7 +186,7 @@ class Plugin {
|
|
|
142
186
|
const replyObj = await this.callTourplan({
|
|
143
187
|
model, endpoint, axios, xmlOptions: defaultXmlOptions,
|
|
144
188
|
});
|
|
145
|
-
assert(R.path(['AuthenticationReply'
|
|
189
|
+
assert(R.path(['AuthenticationReply'], replyObj) === '');
|
|
146
190
|
return true;
|
|
147
191
|
} catch (err) {
|
|
148
192
|
return false;
|
|
@@ -293,7 +337,7 @@ class Plugin {
|
|
|
293
337
|
: await this.cache.getOrExec({
|
|
294
338
|
fnParams: [model],
|
|
295
339
|
fn: () => this.callTourplan(payload),
|
|
296
|
-
ttl: 60 * 60 *
|
|
340
|
+
ttl: 60 * 60 * 24, // 24 hours
|
|
297
341
|
forceRefresh: false,
|
|
298
342
|
});
|
|
299
343
|
let products = [];
|
|
@@ -303,8 +347,8 @@ class Plugin {
|
|
|
303
347
|
products = R.call(R.compose(
|
|
304
348
|
R.map(optionsGroupedBySupplierId => {
|
|
305
349
|
const supplierData = {
|
|
306
|
-
supplierId: R.path([0, 'OptGeneral',
|
|
307
|
-
supplierName: R.path([0, 'OptGeneral',
|
|
350
|
+
supplierId: R.path([0, 'OptGeneral', 'SupplierId'], optionsGroupedBySupplierId),
|
|
351
|
+
supplierName: R.path([0, 'OptGeneral', 'SupplierName'], optionsGroupedBySupplierId),
|
|
308
352
|
};
|
|
309
353
|
return translateTPOption({
|
|
310
354
|
supplierData,
|
|
@@ -314,20 +358,26 @@ class Plugin {
|
|
|
314
358
|
});
|
|
315
359
|
}),
|
|
316
360
|
R.values,
|
|
317
|
-
R.groupBy(R.path(['OptGeneral',
|
|
361
|
+
R.groupBy(R.path(['OptGeneral', 'SupplierId'])),
|
|
318
362
|
productName ? R.filter(o => {
|
|
319
363
|
const str = `${
|
|
320
|
-
R.path(['OptGeneral',
|
|
364
|
+
R.path(['OptGeneral', 'Description'], o)
|
|
321
365
|
} ${
|
|
322
|
-
R.path(['Opt'
|
|
366
|
+
R.path(['Opt'], o)
|
|
323
367
|
} ${
|
|
324
|
-
R.path(['OptGeneral',
|
|
368
|
+
R.path(['OptGeneral', 'SupplierId'], o)
|
|
325
369
|
} ${
|
|
326
|
-
R.path(['OptGeneral',
|
|
370
|
+
R.path(['OptGeneral', 'SupplierName'], o)
|
|
327
371
|
}`;
|
|
328
372
|
return wildcardMatch(productName, str);
|
|
329
373
|
}) : R.identity,
|
|
330
|
-
|
|
374
|
+
root => {
|
|
375
|
+
const options = R.pathOr([], ['OptionInfoReply', 'Option'], root);
|
|
376
|
+
// due to the new parser, single option will be returned as an object
|
|
377
|
+
// instead of an array
|
|
378
|
+
if (Array.isArray(options)) return options;
|
|
379
|
+
return [options];
|
|
380
|
+
},
|
|
331
381
|
), replyObj);
|
|
332
382
|
}
|
|
333
383
|
const productFields = [{
|
|
@@ -715,13 +765,13 @@ class Plugin {
|
|
|
715
765
|
});
|
|
716
766
|
console.log('replyObj', replyObj);
|
|
717
767
|
return {
|
|
718
|
-
message: R.path(['AddServiceReply',
|
|
768
|
+
message: R.path(['AddServiceReply', 'Status'], replyObj)
|
|
719
769
|
=== 'NO' ? 'Service cannot be added to quote' : '',
|
|
720
770
|
quote: {
|
|
721
|
-
id: R.path(['AddServiceReply',
|
|
722
|
-
reference: R.path(['AddServiceReply',
|
|
723
|
-
linePrice: R.path(['AddServiceReply',
|
|
724
|
-
lineId: R.path(['AddServiceReply',
|
|
771
|
+
id: R.path(['AddServiceReply', 'BookingId'], replyObj),
|
|
772
|
+
reference: R.path(['AddServiceReply', 'Ref'], replyObj),
|
|
773
|
+
linePrice: R.path(['AddServiceReply', 'Services', 'Service', 'LinePrice'], replyObj),
|
|
774
|
+
lineId: R.path(['AddServiceReply', 'ServiceLineId'], replyObj),
|
|
725
775
|
},
|
|
726
776
|
};
|
|
727
777
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ti2-tourplan",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Tourplan's TI2 Plugin",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -27,14 +27,17 @@
|
|
|
27
27
|
"testTimeout": 10000
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"axios": "^0.27.2",
|
|
30
31
|
"@graphql-tools/schema": "^8.5.1",
|
|
31
|
-
"graphql": "^16.5.0",
|
|
32
32
|
"bluebird": "^3.7.2",
|
|
33
|
+
"fast-xml-parser": "^4.3.4",
|
|
34
|
+
"graphql": "^16.5.0",
|
|
33
35
|
"js2xmlparser": "^4.0.2",
|
|
34
36
|
"jsonwebtoken": "^8.5.1",
|
|
35
37
|
"moment": "^2.29.1",
|
|
36
38
|
"ramda": "^0.27.1",
|
|
37
39
|
"underscore": "^1.13.4",
|
|
40
|
+
"xml-js": "^1.6.11",
|
|
38
41
|
"xml2js": "^0.4.23"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
package/resolvers/product.js
CHANGED
|
@@ -39,12 +39,12 @@ const translateTPOption = ({ optionsGroupedBySupplierId, supplierData }) => {
|
|
|
39
39
|
productId: supplierData.supplierId,
|
|
40
40
|
productName: supplierData.supplierName,
|
|
41
41
|
// Guides, Accommodation, Transfers, Entrance Fees, Meals, Other
|
|
42
|
-
// category: R.path([0, 'OptGeneral',
|
|
42
|
+
// category: R.path([0, 'OptGeneral', 'ButtonName'], optionsGroupedBySupplierId),
|
|
43
43
|
options: optionsGroupedBySupplierId.map(option => {
|
|
44
|
-
const comment = R.path(['OptGeneral',
|
|
44
|
+
const comment = R.path(['OptGeneral', 'Comment'], option);
|
|
45
45
|
const keyData = {
|
|
46
|
-
optionId: R.path(['Opt'
|
|
47
|
-
optionName: `${R.path(['OptGeneral',
|
|
46
|
+
optionId: R.path(['Opt'], option),
|
|
47
|
+
optionName: `${R.path(['OptGeneral', 'Description'], option)}${
|
|
48
48
|
comment ? `-${comment}` : ''
|
|
49
49
|
}`,
|
|
50
50
|
};
|
|
@@ -60,44 +60,44 @@ const translateTPOption = ({ optionsGroupedBySupplierId, supplierData }) => {
|
|
|
60
60
|
nights is not specified).
|
|
61
61
|
*/
|
|
62
62
|
const units = (() => {
|
|
63
|
-
if (R.path(['OptGeneral',
|
|
63
|
+
if (R.path(['OptGeneral', 'SType'], option) === 'N') {
|
|
64
64
|
return [{
|
|
65
65
|
unitId: 'Adults',
|
|
66
66
|
unitName: 'Adults',
|
|
67
67
|
pricing: [],
|
|
68
68
|
restrictions: {
|
|
69
|
-
paxCount: R.path(['OptGeneral',
|
|
70
|
-
minAge: R.path(['OptGeneral',
|
|
71
|
-
maxAge: R.path(['OptGeneral',
|
|
69
|
+
paxCount: R.path(['OptGeneral', 'MPFCU'], option),
|
|
70
|
+
minAge: R.path(['OptGeneral', 'Adult_From'], option),
|
|
71
|
+
maxAge: R.path(['OptGeneral', 'Adult_To'], option),
|
|
72
72
|
},
|
|
73
|
-
}, R.path(['OptGeneral',
|
|
73
|
+
}, R.path(['OptGeneral', 'ChildrenAllowed'], option) === 'Y' ? {
|
|
74
74
|
unitId: 'Children',
|
|
75
75
|
unitName: 'Children',
|
|
76
76
|
pricing: [],
|
|
77
77
|
restrictions: {
|
|
78
|
-
minAge: R.path(['OptGeneral',
|
|
79
|
-
maxAge: R.path(['OptGeneral',
|
|
78
|
+
minAge: R.path(['OptGeneral', 'Child_From'], option),
|
|
79
|
+
maxAge: R.path(['OptGeneral', 'Child_To'], option),
|
|
80
80
|
},
|
|
81
|
-
} : null, R.path(['OptGeneral',
|
|
81
|
+
} : null, R.path(['OptGeneral', 'InfantsAllowed'], option) === 'Y' ? {
|
|
82
82
|
unitId: 'Infants',
|
|
83
83
|
unitName: 'Infants',
|
|
84
84
|
pricing: [],
|
|
85
85
|
restrictions: {
|
|
86
|
-
minAge: R.path(['OptGeneral',
|
|
87
|
-
maxAge: R.path(['OptGeneral',
|
|
86
|
+
minAge: R.path(['OptGeneral', 'Infant_From'], option),
|
|
87
|
+
maxAge: R.path(['OptGeneral', 'Infant_To'], option),
|
|
88
88
|
},
|
|
89
89
|
} : null].filter(Boolean);
|
|
90
90
|
}
|
|
91
|
-
if (R.path(['OptGeneral',
|
|
91
|
+
if (R.path(['OptGeneral', 'SType'], option) === 'Y') {
|
|
92
92
|
return [['SG', 'Single'], ['TW', 'Twin'], ['DB', 'Double'], ['QD', 'Quad']]
|
|
93
|
-
.filter(([_, unitName]) => R.path(['OptGeneral',
|
|
93
|
+
.filter(([_, unitName]) => R.path(['OptGeneral', `${unitName}_Avail`], option) === 'Y')
|
|
94
94
|
.map(([unitId, unitName]) => ({
|
|
95
95
|
unitId,
|
|
96
96
|
unitName,
|
|
97
97
|
pricing: [],
|
|
98
98
|
restrictions: {
|
|
99
|
-
paxCount: R.path(['OptGeneral',
|
|
100
|
-
|| R.path(['OptGeneral',
|
|
99
|
+
paxCount: R.path(['OptGeneral', `${unitName}_Max`], option)
|
|
100
|
+
|| R.path(['OptGeneral', `${unitName}_Ad_Max`], option),
|
|
101
101
|
},
|
|
102
102
|
}));
|
|
103
103
|
}
|