uapi-json 1.16.0 → 1.16.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uapi-json",
3
- "version": "1.16.0",
3
+ "version": "1.16.1",
4
4
  "description": "Travelport Universal API",
5
5
  "main": "src/",
6
6
  "files": [
@@ -49,14 +49,14 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "chai": "^4.3.7",
52
- "eslint": "^8.29.0",
53
- "eslint-config-airbnb-base": "^13.2.0",
54
- "eslint-plugin-import": "^2.25.3",
52
+ "eslint": "^8.39.0",
53
+ "eslint-config-airbnb-base": "^15.0.0",
54
+ "eslint-plugin-import": "^2.29.0",
55
55
  "mocha": "^10.2.0",
56
56
  "nyc": "^15.1.0",
57
57
  "proxyquire": "^2.1.3",
58
58
  "readline2": "^1.0.1",
59
- "sinon": "^15.0.1",
59
+ "sinon": "^16.1.1",
60
60
  "sinon-chai": "^3.7.0"
61
61
  }
62
62
  }
@@ -51,7 +51,6 @@ module.exports = function defaultConfig(ver) {
51
51
  // air:AirSegment
52
52
  // universal:ProviderReservationInfo
53
53
 
54
-
55
54
  // Some single-object arrays with one key can be safely collapsed into an object or a scalar,
56
55
  // because they would never have a second item or any more fields in objects
57
56
  // NOTE: if such array name ends in list, e.g. FlightOptionsList (with FlightOption item),
@@ -7,7 +7,7 @@ const {
7
7
  RequestRuntimeError,
8
8
  } = require('./RequestErrors');
9
9
 
10
- const parseString = xml => new Promise((resolve, reject) => {
10
+ const parseString = (xml) => new Promise((resolve, reject) => {
11
11
  xml2js.parseString(xml, (err, json) => {
12
12
  if (err) {
13
13
  reject(err);
@@ -33,7 +33,7 @@ function mergeLeaf(item) {
33
33
  delete (item.$);
34
34
  // item.renameProperty('_', 'text'); //TODO decide if _ is the best name
35
35
 
36
- return Object.assign({}, item, leaf);
36
+ return { ...item, ...leaf };
37
37
  }
38
38
 
39
39
  function Parser(root, uapiVersion, env, debug, config, provider) {
@@ -71,7 +71,7 @@ Parser.prototype.mapArrayKeys = function mapArrayKeys(array, name) {
71
71
  }
72
72
 
73
73
  if (!hasAllKeys) {
74
- return array.map(value => self.mergeLeafRecursive(value, name /* + ':' + key */));
74
+ return array.map((value) => self.mergeLeafRecursive(value, name /* + ':' + key */));
75
75
  }
76
76
 
77
77
  const object = array
@@ -155,18 +155,18 @@ Parser.prototype.parseVersion = function (obj) {
155
155
  const detail = obj['SOAP:Fault'][0].detail[0];
156
156
  const parsedVersions = Object.keys(detail)
157
157
  .filter(
158
- key => key.match(/ErrorInfo$/i)
158
+ (key) => key.match(/ErrorInfo$/i)
159
159
  )
160
160
  .reduce(
161
161
  (acc, key) => acc.concat(
162
162
  Object.keys(detail[key][0].$)
163
- .filter(detailKey => detailKey.match(/^xmlns/i))
164
- .map(detailKey => detail[key][0].$[detailKey])
163
+ .filter((detailKey) => detailKey.match(/^xmlns/i))
164
+ .map((detailKey) => detail[key][0].$[detailKey])
165
165
  ),
166
166
  []
167
167
  )
168
- .map(xmlns => xmlns.match(/_(v\d+_\d+)$/))
169
- .filter(version => version !== null);
168
+ .map((xmlns) => xmlns.match(/_(v\d+_\d+)$/))
169
+ .filter((version) => version !== null);
170
170
  return parsedVersions[0][1];
171
171
  };
172
172
 
@@ -182,14 +182,13 @@ module.exports = function uapiRequest(
182
182
  return result;
183
183
  };
184
184
 
185
-
186
185
  return validateInput()
187
186
  .then(handlebars.compile)
188
- .then(template => prepareRequest(template, auth, params))
187
+ .then((template) => prepareRequest(template, auth, params))
189
188
  .then(sendRequest)
190
189
  .then(parseResponse)
191
190
  .then(validateSOAP)
192
- .then(res => parseFunction.call(uParser, res, params))
191
+ .then((res) => parseFunction.call(uParser, res, params))
193
192
  .then(handleSuccess);
194
193
  };
195
194
  };
@@ -63,7 +63,7 @@ module.exports = (settings) => {
63
63
  ),
64
64
  };
65
65
 
66
- return service.addSegments(Object.assign({}, options, missedOptions));
66
+ return service.addSegments({ ...options, ...missedOptions });
67
67
  });
68
68
  }
69
69
 
@@ -74,10 +74,12 @@ module.exports = (settings) => {
74
74
  return service.airPricePricingSolutionXML(options).then((data) => {
75
75
  const tauDate = moment(options.tau || null);
76
76
  const tau = tauDate.isValid() ? tauDate.format() : moment().add(3, 'hours').format();
77
- const bookingParams = Object.assign({}, {
77
+ const bookingParams = {
78
78
  ticketDate: tau,
79
79
  ActionStatusType: 'TAU',
80
- }, data, options);
80
+ ...data,
81
+ ...options
82
+ };
81
83
  return service.createReservation(bookingParams).catch((err) => {
82
84
  if (err instanceof AirRuntimeError.SegmentBookingFailed
83
85
  || err instanceof AirRuntimeError.NoValidFare) {
@@ -97,7 +99,7 @@ module.exports = (settings) => {
97
99
  getBooking(options) {
98
100
  return this.getUniversalRecordByPNR(options)
99
101
  .then(
100
- ur => getBookingFromUr(ur, options.pnr)
102
+ (ur) => getBookingFromUr(ur, options.pnr)
101
103
  || Promise.reject(new AirRuntimeError.NoPNRFoundInUR(ur))
102
104
  )
103
105
  .then((data) => {
@@ -186,7 +188,7 @@ module.exports = (settings) => {
186
188
  .catch((err) => {
187
189
  if (err instanceof AirRuntimeError.TicketingFoidRequired) {
188
190
  return this.getBooking(options)
189
- .then(updatedBooking => service.foid(updatedBooking))
191
+ .then((updatedBooking) => service.foid(updatedBooking))
190
192
  .then(() => again(err));
191
193
  }
192
194
  if (err instanceof AirRuntimeError.TicketingPNRBusy) {
@@ -206,7 +208,7 @@ module.exports = (settings) => {
206
208
 
207
209
  async getTicketFromTicketsList(pnr, ticketNumber) {
208
210
  const tickets = await this.getTickets({ pnr });
209
- return tickets.find(t => t.ticketNumber === ticketNumber);
211
+ return tickets.find((t) => t.ticketNumber === ticketNumber);
210
212
  },
211
213
 
212
214
  retryableTicketErrorHandlers: {
@@ -250,7 +252,7 @@ module.exports = (settings) => {
250
252
  }
251
253
  }));
252
254
 
253
- const [ticket = null] = results.filter(result => result);
255
+ const [ticket = null] = results.filter((result) => result);
254
256
  return ticket;
255
257
  }
256
258
  },
@@ -331,7 +333,7 @@ module.exports = (settings) => {
331
333
  .then(() => ({ ...line, pnr }));
332
334
  });
333
335
  }))
334
- .then(data => ({ type: 'list', data }));
336
+ .then((data) => ({ type: 'list', data }));
335
337
  }
336
338
 
337
339
  const pnr = parsers.bookingPnr(firstScreen);
@@ -346,7 +348,7 @@ module.exports = (settings) => {
346
348
  new AirRuntimeError.MissingPaxListAndBooking(firstScreen)
347
349
  );
348
350
  })
349
- .then(results => terminal.closeSession()
351
+ .then((results) => terminal.closeSession()
350
352
  .then(() => results)
351
353
  .catch(() => results));
352
354
  },
@@ -354,13 +356,13 @@ module.exports = (settings) => {
354
356
  cancelTicket(options) {
355
357
  return this.getTicket(options)
356
358
  .then(
357
- ticketData => service.cancelTicket({
359
+ (ticketData) => service.cancelTicket({
358
360
  pnr: ticketData.pnr,
359
361
  ticketNumber: options.ticketNumber,
360
362
  })
361
363
  )
362
364
  .catch(
363
- err => Promise.reject(new AirRuntimeError.FailedToCancelTicket(options, err))
365
+ (err) => Promise.reject(new AirRuntimeError.FailedToCancelTicket(options, err))
364
366
  );
365
367
  },
366
368
 
@@ -371,12 +373,12 @@ module.exports = (settings) => {
371
373
  pnr,
372
374
  } = options;
373
375
 
374
- const checkTickets = tickets => Promise.all(tickets.map(
376
+ const checkTickets = (tickets) => Promise.all(tickets.map(
375
377
  (ticketData) => {
376
378
  // Check for VOID or REFUND
377
379
  const allTicketsVoidOrRefund = ticketData.tickets.every(
378
- ticket => ticket.coupons.every(
379
- coupon => coupon.status === 'V' || coupon.status === 'R'
380
+ (ticket) => ticket.coupons.every(
381
+ (coupon) => coupon.status === 'V' || coupon.status === 'R'
380
382
  )
381
383
  );
382
384
 
@@ -389,8 +391,8 @@ module.exports = (settings) => {
389
391
  }
390
392
  // Check for not OPEN/VOID segments
391
393
  const hasNotOpenSegment = ticketData.tickets.some(
392
- ticket => ticket.coupons.some(
393
- coupon => 'OV'.indexOf(coupon.status) === -1
394
+ (ticket) => ticket.coupons.some(
395
+ (coupon) => 'OV'.indexOf(coupon.status) === -1
394
396
  )
395
397
  );
396
398
 
@@ -400,7 +402,7 @@ module.exports = (settings) => {
400
402
 
401
403
  return Promise.all(
402
404
  ticketData.tickets.map(
403
- ticket => (
405
+ (ticket) => (
404
406
  ticket.coupons[0].status !== 'V'
405
407
  ? service.cancelTicket({ pnr, ticketNumber: ticket.ticketNumber })
406
408
  : Promise.resolve(true)
@@ -431,7 +433,7 @@ module.exports = (settings) => {
431
433
 
432
434
  getExchangeInformation(options) {
433
435
  return this.getBooking(options)
434
- .then(booking => service.exchangeQuote({
436
+ .then((booking) => service.exchangeQuote({
435
437
  ...options,
436
438
  bookingDate: moment(booking.createdAt).format('YYYY-MM-DD'),
437
439
  }));
@@ -439,7 +441,7 @@ module.exports = (settings) => {
439
441
 
440
442
  exchangeBooking(options) {
441
443
  return this.getBooking(options)
442
- .then(booking => service.exchangeBooking({
444
+ .then((booking) => service.exchangeBooking({
443
445
  ...options,
444
446
  uapi_reservation_locator: booking.uapi_reservation_locator,
445
447
  }));
@@ -90,6 +90,7 @@ Object.assign(AirParsingError, createErrorsList({
90
90
  CancelResponseNotFound: 'Cancel response not found',
91
91
  InvalidServiceSegmentFormat: 'Service segment format is invalid',
92
92
  NoProviderSegmentOrder: 'Segment is missing ProviderSegmentOrder field',
93
+ NoTravelOrder: 'Segment is missing TravelOrder field',
93
94
  }, AirParsingError));
94
95
 
95
96
  // Runtime errors
@@ -134,16 +134,15 @@ function formatPrices(prices) {
134
134
  };
135
135
  }
136
136
 
137
-
138
137
  function formatTrip(segment, flightDetails) {
139
138
  const flightInfo = flightDetails
140
139
  ? Object.keys(flightDetails).map(
141
- detailsKey => flightDetails[detailsKey]
140
+ (detailsKey) => flightDetails[detailsKey]
142
141
  )
143
142
  : [];
144
- const plane = flightInfo.map(details => details.Equipment || 'Unknown');
145
- const duration = flightInfo.map(details => details.FlightTime || 0);
146
- const techStops = flightInfo.slice(1).map(details => details.Origin);
143
+ const plane = flightInfo.map((details) => details.Equipment || 'Unknown');
144
+ const duration = flightInfo.map((details) => details.FlightTime || 0);
145
+ const techStops = flightInfo.slice(1).map((details) => details.Origin);
147
146
 
148
147
  segment['air:FlightDetails'] = flightInfo;
149
148
 
@@ -208,17 +207,15 @@ function formatPassengerCategories(pricingInfo) {
208
207
  }, {});
209
208
 
210
209
  const passengerFares = Object.keys(passengerCategories)
211
- .reduce(
212
- (memo, ptc) => Object.assign(memo, {
213
- [ptc]: {
214
- totalPrice: passengerCategories[ptc].TotalPrice,
215
- basePrice: passengerCategories[ptc].BasePrice,
216
- equivalentBasePrice: passengerCategories[ptc].EquivalentBasePrice,
217
- taxes: passengerCategories[ptc].Taxes,
218
- fareCalc: passengerCategories[ptc].FareCalc,
219
- },
220
- }), {}
221
- );
210
+ .reduce((memo, ptc) => Object.assign(memo, {
211
+ [ptc]: {
212
+ totalPrice: passengerCategories[ptc].TotalPrice,
213
+ basePrice: passengerCategories[ptc].BasePrice,
214
+ equivalentBasePrice: passengerCategories[ptc].EquivalentBasePrice,
215
+ taxes: passengerCategories[ptc].Taxes,
216
+ fareCalc: passengerCategories[ptc].FareCalc,
217
+ },
218
+ }), {});
222
219
 
223
220
  return {
224
221
  passengerCounts,
@@ -325,9 +322,9 @@ function formatLowFaresSearch(searchRequest, searchResult) {
325
322
  const trips = leg['air:AirSegmentRef'].map((segmentRef) => {
326
323
  const segment = segments[segmentRef];
327
324
 
328
- const tripFlightDetails = segment['air:FlightDetailsRef'].map(flightDetailsRef => flightDetails[flightDetailsRef]);
325
+ const tripFlightDetails = segment['air:FlightDetailsRef'].map((flightDetailsRef) => flightDetails[flightDetailsRef]);
329
326
 
330
- const [bookingInfo] = thisFare['air:BookingInfo'].filter(info => info.SegmentRef === segmentRef);
327
+ const [bookingInfo] = thisFare['air:BookingInfo'].filter((info) => info.SegmentRef === segmentRef);
331
328
  const fareInfo = fareInfos[bookingInfo.FareInfoRef];
332
329
 
333
330
  const seatsAvailable = Number(bookingInfo.BookingCount);
@@ -354,13 +351,13 @@ function formatLowFaresSearch(searchRequest, searchResult) {
354
351
  }];
355
352
  });
356
353
  } else {
357
- directions = thisFare['air:FlightOptionsList'].map(direction => Object.values(direction['air:Option']).map((option) => {
354
+ directions = thisFare['air:FlightOptionsList'].map((direction) => Object.values(direction['air:Option']).map((option) => {
358
355
  const trips = option['air:BookingInfo'].map(
359
356
  (segmentInfo) => {
360
357
  const fareInfo = fareInfos[segmentInfo.FareInfoRef];
361
358
  const segment = segments[segmentInfo.SegmentRef];
362
359
  const tripFlightDetails = segment['air:FlightDetailsRef'].map(
363
- flightDetailsRef => flightDetails[flightDetailsRef]
360
+ (flightDetailsRef) => flightDetails[flightDetailsRef]
364
361
  );
365
362
  const seatsAvailable = (
366
363
  segment['air:AirAvailInfo']
@@ -438,23 +435,54 @@ function formatLowFaresSearch(searchRequest, searchResult) {
438
435
  return fares;
439
436
  }
440
437
 
441
- const getSegmentsData = segmentsObject => (segmentsObject
438
+ const getSegmentsData = (segmentsObject) => (segmentsObject
442
439
  ? Object.values(segmentsObject)
443
440
  : null);
444
441
 
445
- const setIndexes = segments => segments.map(
446
- (segment) => {
447
- const { ProviderSegmentOrder: index } = segment;
448
- if (index === undefined) {
449
- throw new AirParsingError.NoProviderSegmentOrder({
450
- segment,
451
- });
452
- }
442
+ const setIndexes = (segments) => {
443
+ return segments
444
+ // Adding index and travelOrder fields
445
+ .map((segment) => {
446
+ const { ProviderSegmentOrder: index, TravelOrder: travelOrder } = segment;
447
+ if (index === undefined) {
448
+ throw new AirParsingError.NoProviderSegmentOrder({
449
+ segment,
450
+ });
451
+ }
452
+ if (travelOrder === undefined) {
453
+ throw new AirParsingError.NoTravelOrder({
454
+ segment,
455
+ });
456
+ }
457
+ return { ...segment, index: parseInt(index, 10), travelOrder: parseInt(travelOrder, 10) };
458
+ })
459
+ // Sorting segments in order to remove possible duplicates effectively
460
+ .sort((a, b) => {
461
+ // Get segments data required for comparison
462
+ const { index: aIndex, travelOrder: aTravelOrder } = a;
463
+ const { index: bIndex, travelOrder: bTravelOrder } = b;
464
+ const travelOrderDiff = aTravelOrder - bTravelOrder;
465
+ const indexDiff = aIndex - bIndex;
466
+
467
+ // Comparing provider order (index)
468
+ if (indexDiff !== 0) {
469
+ return indexDiff;
470
+ }
453
471
 
454
- return { ...segment, index: parseInt(index, 10) };
455
- }
456
- );
472
+ // Provider order is the same, possible duplicates, comparing travel order
473
+ // Travel order could be some high number, regardless of low provirder order (index)
474
+ if (travelOrderDiff !== 0) {
475
+ return travelOrderDiff;
476
+ }
457
477
 
478
+ // Travel order is the same, most probably duplicates, first in list goes first
479
+ return 0;
480
+ })
481
+ // Removing duplicates
482
+ .filter((segment, i, arr) => {
483
+ return arr.findIndex((el) => el.index === segment.index) === i;
484
+ });
485
+ };
458
486
 
459
487
  /**
460
488
  * This function used to transform segments and service segments objects
@@ -487,22 +515,20 @@ function setIndexesForSegments(
487
515
  }
488
516
 
489
517
  function buildPassenger(name, traveler) {
490
- return Object.assign(
491
- {
492
- lastName: name.Last,
493
- firstName: name.First,
494
- uapi_passenger_ref: traveler.Key,
495
- },
496
- traveler.DOB ? {
518
+ return {
519
+ lastName: name.Last,
520
+ firstName: name.First,
521
+ uapi_passenger_ref: traveler.Key,
522
+ ...(traveler.DOB ? {
497
523
  birthDate: moment(traveler.DOB).format('YYYY-MM-DD'),
498
- } : null,
499
- traveler.TravelerType ? {
524
+ } : null),
525
+ ...(traveler.TravelerType ? {
500
526
  ageCategory: traveler.TravelerType,
501
- } : null,
502
- traveler.Gender ? {
527
+ } : null),
528
+ ...(traveler.Gender ? {
503
529
  gender: traveler.Gender,
504
- } : null
505
- );
530
+ } : null)
531
+ };
506
532
  }
507
533
 
508
534
  /**