sabre-client 0.4.1 → 0.5.0
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/README.es.md +202 -21
- package/README.md +202 -21
- package/dist/index.d.ts +2726 -693
- package/dist/index.js +495 -115
- package/dist/index.js.map +1 -1
- package/dist/reservation-history-bKDqkf8T.d.ts +788 -0
- package/dist/testing.d.ts +129 -7
- package/dist/testing.js +475 -57
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/queue-ref-BDGh8FrV.d.ts +0 -11
package/README.md
CHANGED
|
@@ -12,17 +12,31 @@ TypeScript SDK for the Sabre APIs.
|
|
|
12
12
|
|
|
13
13
|
## Status
|
|
14
14
|
|
|
15
|
-
`0.
|
|
16
|
-
flow over REST,
|
|
17
|
-
|
|
15
|
+
`0.5.0` is published under the `next` dist-tag while it is tested; `latest` stays on `0.4.1`.
|
|
16
|
+
It covers Tier 1 and Tier 2 of the Air Booking flow over REST, Tier 3 (SOAP sessions and queues)
|
|
17
|
+
and Tier 4: queue activity, PNR history, the `S` message queue, `selection` in `list` and
|
|
18
|
+
`onRawRequest`. It has three breaking changes (see [Upgrading from 0.4](#upgrading-from-04)).
|
|
18
19
|
|
|
19
20
|
```sh
|
|
20
|
-
npm install sabre-client
|
|
21
|
+
npm install sabre-client@next # 0.5.0
|
|
22
|
+
npm install sabre-client # 0.4.1
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
The package is still `0.x`: a minor version may include breaking changes, and the
|
|
24
26
|
[CHANGELOG](./CHANGELOG.md) marks them.
|
|
25
27
|
|
|
28
|
+
### Upgrading from 0.4
|
|
29
|
+
|
|
30
|
+
- **`pcc` is no longer in the config.** Passing it to `new Sabre` or `new SabreLegacy` is a type
|
|
31
|
+
error, and in JavaScript it is ignored. The PCC you authenticate with goes in `credentials`
|
|
32
|
+
(see [Credentials](#credentials)); another PCC goes per operation, as `targetPcc` in REST or
|
|
33
|
+
`pcc` in the queue operations.
|
|
34
|
+
- **`QueueRef` can be a `string`.** An alphabetic queue like `'S'` is `'S'`, and `count` and
|
|
35
|
+
`place` return it that way instead of `{ name: 'S' }`. Code that does
|
|
36
|
+
`typeof q === 'number' ? … : q.name` must handle the `string` case.
|
|
37
|
+
- **A `QueueStep` that is not done has a `kind`.** Check `step.kind === 'pnr'` before reading
|
|
38
|
+
`step.item`; `kind: 'message'` carries a message of the `S` queue.
|
|
39
|
+
|
|
26
40
|
Tier 1 covers the core Air Booking flow over REST:
|
|
27
41
|
|
|
28
42
|
| Step | Service | Method |
|
|
@@ -45,11 +59,12 @@ Tier 3 adds the `SabreLegacy` SOAP client, with a stateful session and Queue Man
|
|
|
45
59
|
| Step | Service | Method |
|
|
46
60
|
| - | - | - |
|
|
47
61
|
| Session | `SessionCreateRQ` 2.0.0 / 1.0.0, `OTA_PingRQ`, `SessionCloseRQ` | `legacy.withSession`, `legacy.openSession` |
|
|
48
|
-
| Queues | Get Queue Count, Access Queue, Place Queue Message | `session.queue.count`, `list`, `access`, `ignore`, `remove`, `place`, `exit`, `process` |
|
|
49
|
-
| Queue paragraph | — (pure function) | `parseQueueParagraph`, `resolveHostDate` |
|
|
62
|
+
| Queues | Get Queue Count, Get Queue Activity, Access Queue, Place Queue Message | `session.queue.count`, `activity`, `list`, `access`, `ignore`, `remove`, `place`, `exit`, `process` |
|
|
63
|
+
| Queue paragraph | — (pure function) | `parseQueueParagraph`, `parseQueuePlaceRemark`, `resolveHostDate` |
|
|
64
|
+
| PNR history | `TravelItineraryHistoryLLSRQ` 2.4.0 | `session.reservation.history` |
|
|
50
65
|
| Host commands | `SabreCommandLLSRQ` 2.0.0 | `session.command` |
|
|
51
66
|
|
|
52
|
-
Planned after `0.
|
|
67
|
+
Planned after `0.5.0`: Get Ancillaries (NDC only).
|
|
53
68
|
|
|
54
69
|
## Requirements
|
|
55
70
|
|
|
@@ -88,11 +103,6 @@ const sabre = new Sabre({
|
|
|
88
103
|
| `fetch` | global `fetch` | Custom `fetch` (proxies, tests, other runtimes) |
|
|
89
104
|
| `tokenStore` | in-memory only | Shares the token between processes (see [Token reuse](#token-reuse)) |
|
|
90
105
|
|
|
91
|
-
> **`pcc` is deprecated** since `0.4.1` and will be removed in `0.5.0`. It was a reserved field,
|
|
92
|
-
> and no client reads it. The PCC you authenticate with goes in `credentials` (see
|
|
93
|
-
> [Credentials](#credentials) and `buildCredentials`). To work in another PCC, pass it per
|
|
94
|
-
> operation: `targetPcc` in REST, `pcc` in the queue operations.
|
|
95
|
-
|
|
96
106
|
### Credentials
|
|
97
107
|
|
|
98
108
|
There is **one set of credentials** for REST and SOAP: the same `credentials` object works for
|
|
@@ -124,7 +134,9 @@ The object that `buildCredentials` returns carries its `authVersion`, so the con
|
|
|
124
134
|
need to repeat it: with `authVersion: 'v2'` in `buildCredentials`, `{ environment, credentials }`
|
|
125
135
|
is already a v2 config. If you pass `authVersion` in the config too, it must match, or the client
|
|
126
136
|
throws a `TypeError`. Credentials you build by hand have no `authVersion`, so with v2 the config
|
|
127
|
-
still needs `authVersion: 'v2'`.
|
|
137
|
+
still needs `authVersion: 'v2'`. If the version is only known at runtime (for example, read
|
|
138
|
+
from an environment variable into a `BuildCredentialsOptions`), the result of `buildCredentials`
|
|
139
|
+
can still go straight into the config, with no `authVersion` there.
|
|
128
140
|
|
|
129
141
|
The PCC in the credentials is the one you authenticate with. Working in another PCC is not a
|
|
130
142
|
second set of credentials: pass it per operation, as `targetPcc` in `booking.create`, `get` and
|
|
@@ -360,6 +372,7 @@ const screen = await legacy.withSession((session) => session.command('W/PT¥*',
|
|
|
360
372
|
// screen.text → 'PSGR TYPE - ON' (as Sabre sends it, not trimmed)
|
|
361
373
|
// screen.warnings → the stl:Warning entries, if any
|
|
362
374
|
// screen.messageId → eb:MessageId, for Sabre support
|
|
375
|
+
// screen.respondedAt → eb:Timestamp in UTC, e.g. '2026-11-06T17:34:16Z'
|
|
363
376
|
```
|
|
364
377
|
|
|
365
378
|
- **The command is sent as is.** In this service Sabre's `‡` separator is written `¥`
|
|
@@ -384,16 +397,63 @@ const screen = await legacy.withSession((session) => session.command('W/PT¥*',
|
|
|
384
397
|
characters (`W/PT`, `DC`, `*IM`), never the whole command or the screen. The full response is
|
|
385
398
|
available through `onRawResponse`.
|
|
386
399
|
|
|
400
|
+
### PNR history
|
|
401
|
+
|
|
402
|
+
`session.reservation.history(locator, options?)` reads the PNR history (Get Itinerary History,
|
|
403
|
+
`TravelItineraryHistoryLLSRQ` 2.4.0, the host's `*H`), one entry per transaction:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
const { transactions } = await legacy.withSession((session) => session.reservation.history('ABC123'));
|
|
407
|
+
// transactions[0] → { rph, createdAt: '2026-11-04T17:15', agent, agentDutyCode, pcc, aaaPcc,
|
|
408
|
+
// receivedFrom, flightSegments, generalInfo, remarks, accounting }
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
- **Who, when and from where**: `createdAt` (with a year, no time zone), the agent's sine and duty
|
|
412
|
+
code, the signing `pcc`, the AAA's `aaaPcc` and `receivedFrom`.
|
|
413
|
+
- **What changed, with its `action`**: `flightSegments` with their status at that moment
|
|
414
|
+
(`AS` added, `XS` cancelled; dates without a year, as the host sends them), and SSR, ticketing,
|
|
415
|
+
phones, names and emails as text in `generalInfo` (`A4S`, `A7`, `A9`, `AN`, `APE`…). Sabre does
|
|
416
|
+
not publish the list of `action` values. Seats, cars, hotels, rail, insurance and invoices are
|
|
417
|
+
not typed: they stay in the raw response (`onRawResponse`).
|
|
418
|
+
- **It carries passenger data** (names, phones, emails). The SDK never logs it.
|
|
419
|
+
- **It does not record queue placements**, neither the system's nor a manual `place` (CERT): for that, use
|
|
420
|
+
`queue.list`/`access` or the `QUEUE_PLACE` remarks of Get Booking.
|
|
421
|
+
- **Call it outside a queue walk.** Inside a queue, Sabre rejects the history of another PNR with
|
|
422
|
+
`NAK6 -NOT ALLOWED- FINISH OR IGNORE OTHER ACTIVITY`, a `SabreBusinessError` (CERT). The PNR in
|
|
423
|
+
the AAA does not change, `queue.state` stays `'inside'`, and the walk goes on with `ignore`.
|
|
424
|
+
- It is a read: it is retried. A `locator` that is not 6 uppercase alphanumeric characters throws a
|
|
425
|
+
`TypeError` without calling the network. A PNR that does not exist throws a `SabreBusinessError`
|
|
426
|
+
(`service: 'itineraryHistory'`).
|
|
427
|
+
|
|
387
428
|
### Queues
|
|
388
429
|
|
|
389
430
|
The queue operations live in `session.queue`. `pcc` is optional in every call and defaults to the
|
|
390
|
-
session's PCC. A queue
|
|
391
|
-
|
|
431
|
+
session's PCC. A queue (`QueueRef`) comes in three forms:
|
|
432
|
+
|
|
433
|
+
| Queue | `QueueRef` | Sent as |
|
|
434
|
+
| - | - | - |
|
|
435
|
+
| Numeric, 0 to 511 | `400` | `Number="400"` |
|
|
436
|
+
| Alphabetic, one or two uppercase letters | `'S'` | `Number="S"` |
|
|
437
|
+
| Named (QN and QNB tables) | `{ name: 'HOTELS' }` | `Name="HOTELS"` |
|
|
438
|
+
|
|
439
|
+
The queues returned by `count` can be passed straight to the other operations. `'S'` is the PCC's
|
|
440
|
+
**message queue**: it holds messages, not PNRs, and Sabre does not list it, so `list({ queue: 'S' })`
|
|
441
|
+
throws a `TypeError` without calling Sabre. Walk it by hand: `access({ queue: 'S' })` returns the
|
|
442
|
+
first message (`kind: 'message'`) and `ignore()` the next one, leaving it in the queue. `process`
|
|
443
|
+
and `place` inside `S` throw a `TypeError` without calling Sabre. `remove()` sends `QR`, but there
|
|
444
|
+
is no evidence yet of what Sabre does with the message.
|
|
445
|
+
|
|
446
|
+
> **Migrating from 0.4.x**: `count` used to return the `S` queue as `{ name: 'S' }`, which Sabre
|
|
447
|
+
> rejects (`INVALID QUEUE NAME FORMAT`). It now returns `'S'`. Code that does
|
|
448
|
+
> `typeof q === 'number' ? … : q.name` no longer compiles, because `q` can be a `string`. A
|
|
449
|
+
> `{ name }` of one or two uppercase letters now throws a `TypeError` that tells you to pass the
|
|
450
|
+
> letter instead, and `'12'` or `'s'` throw too: each queue has a single form.
|
|
392
451
|
|
|
393
452
|
| Method | Sabre service | Returns |
|
|
394
453
|
| - | - | - |
|
|
395
454
|
| `count({ pcc? })` | `QueueCountLLSRQ` | `{ queues: [{ queue, count }], totals, countedAt, warnings }` |
|
|
396
|
-
| `
|
|
455
|
+
| `activity({ pcc? })` | `QueueAnalysisLLSRQ` | `{ queues: [{ queue, numMessages, ended, ignored, … }], totals, analyzedAt, warnings }` |
|
|
456
|
+
| `list({ queue, pcc?, primaryPassenger?, selection? })` | `QueueAccessLLSRQ` (`List`) | `{ locators, entries, paragraph, warnings }`, without entering the queue |
|
|
397
457
|
| `access({ queue, pcc?, selection? })` | `QueueAccessLLSRQ` | `QueueStep` |
|
|
398
458
|
| `ignore()` | `QueueAccessLLSRQ` (`I`) | `QueueStep` |
|
|
399
459
|
| `remove({ from? })` | `QueueAccessLLSRQ` (`QR`) | `QueueStep` |
|
|
@@ -402,10 +462,30 @@ session's PCC. A queue is a number from 0 to 511 or `{ name }` for alphabetic qu
|
|
|
402
462
|
|
|
403
463
|
```ts
|
|
404
464
|
type QueueStep =
|
|
405
|
-
| { done: false; item: QueueItem; warnings: SabreWarning[] }
|
|
465
|
+
| { done: false; kind: 'pnr'; item: QueueItem; warnings: SabreWarning[] }
|
|
466
|
+
| { done: false; kind: 'message'; message: { text: string[] }; warnings: SabreWarning[] } // queue 'S'
|
|
406
467
|
| { done: true; reason: 'end-of-display' | 'empty'; message: string; warnings: SabreWarning[] };
|
|
407
468
|
```
|
|
408
469
|
|
|
470
|
+
> **Migrating from 0.4.x**: a step that is not done now has a `kind`. Check `step.kind === 'pnr'`
|
|
471
|
+
> before reading `step.item`. Only the `S` queue returns `kind: 'message'`.
|
|
472
|
+
|
|
473
|
+
- **`list` returns the whole queue in one response**: there is no paging, even with more than 1000
|
|
474
|
+
PNRs (1221 in about 279 KB, in production). `selection` narrows it, but in the list Sabre only
|
|
475
|
+
accepts `QD` (date placed on the queue, e.g. `{ function: 'QD', criteria: '05NOV' }`) or `AK`
|
|
476
|
+
(the agent who placed it, PCC plus sine, e.g. `'PCC1AAT'`; not tested in the list yet).
|
|
477
|
+
Any other code throws a `TypeError` without calling Sabre. The `placedAt` of a list entry is
|
|
478
|
+
when the PNR was placed on the queue, the date `QD` filters on.
|
|
479
|
+
- **`activity`** returns, per queue, the eight counters of the host's `QA` screen: `numMessages`
|
|
480
|
+
(on the queue now), `ended`, `ignored`, `moved`, `removed`, `transferred`,
|
|
481
|
+
`leftMessageToContact` and `unableToReach`, plus `totals` for the PCC. Sabre also lists empty
|
|
482
|
+
queues that have counters. No source says since when the counters run, so the SDK returns them
|
|
483
|
+
as they come. Like `count`, it is a read: it is retried, it works inside or outside a queue, and
|
|
484
|
+
it does not need duty code 9.
|
|
485
|
+
- **Every queue result carries `respondedAt`**: Sabre's `eb:Timestamp`, in ISO 8601 UTC
|
|
486
|
+
(`'2026-11-06T17:34:16Z'`). Unlike `countedAt` and `placedAt`, which come as the host sends them
|
|
487
|
+
(no year, PCC local time), it has a year and a zone. It is missing only if Sabre does not send it.
|
|
488
|
+
|
|
409
489
|
- **The end of the queue is a result, not an error.** `END OF DISPLAY FOR REQUESTED DATA` gives
|
|
410
490
|
`{ done: true, reason: 'end-of-display' }`, and an empty queue (`QUEUE SELECTED WAS EMPTY`)
|
|
411
491
|
gives `reason: 'empty'`. In both cases the session is left outside the queue. Any other Sabre
|
|
@@ -425,7 +505,9 @@ type QueueStep =
|
|
|
425
505
|
Production rejects a `place` without it, although the design says it is optional.
|
|
426
506
|
- **`QueueItem`** has `locator`, the `placedAt` and `placedBy` of `Line`, `primaryPassenger` and
|
|
427
507
|
`group` when Sabre sends them, and `paragraph`: the PNR in host format, line by line and
|
|
428
|
-
untouched. `placedAt` is the text that arrived, with or without the year.
|
|
508
|
+
untouched. `placedAt` is the text that arrived, with or without the year. Inside a queue it is
|
|
509
|
+
the PNR's last change, not when it was placed on the queue: that one is the `placedAt` of
|
|
510
|
+
`list`, and the two differ for the same PNR. `resolveHostDate`
|
|
429
511
|
turns it into a full date.
|
|
430
512
|
- `count` inside a queue does not count the PNR on screen.
|
|
431
513
|
|
|
@@ -466,6 +548,20 @@ segments with their status, the ticketing field with an `issued` flag, and the r
|
|
|
466
548
|
- It does not read deadlines that airlines write as free text in remarks, SSR or OSI. Their format
|
|
467
549
|
belongs to each airline. The text is still in `paragraph`.
|
|
468
550
|
|
|
551
|
+
### Reading a `QUEUE_PLACE` remark
|
|
552
|
+
|
|
553
|
+
Get Booking returns where the PNR was placed as a remark with `type: 'QUEUE_PLACE'`. Its `text`
|
|
554
|
+
packs the PCC, the date, the queue and the PIC with no separators. `parseQueuePlaceRemark(text)`
|
|
555
|
+
splits it:
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
parseQueuePlaceRemark('MEX25SEP146/015'); // { pcc: 'MEX', date: '25SEP', queue: 146, pic: 15 }
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
It returns `null` if the text does not have that shape, or if the day, month, queue (0–511) or PIC
|
|
562
|
+
(0–191, 200–254) are out of range. `date` has no year: resolve it with
|
|
563
|
+
`resolveHostDate(date, referenceDate)`. Reading the remarks from the booking is up to you.
|
|
564
|
+
|
|
469
565
|
### Testing your queue logic: `sabre-client/testing`
|
|
470
566
|
|
|
471
567
|
`sabre-client/testing` is a separate entry point with a fake of Sabre's sessions and queues. It is
|
|
@@ -496,16 +592,67 @@ fake.openSessions; // 0: the session was closed
|
|
|
496
592
|
`{ locator, paragraph?, placedAt?, placedBy?, primaryPassenger? }`. A paragraph without a
|
|
497
593
|
signature line gets a synthetic one. `pnrs` adds PNRs that are not in any queue, for
|
|
498
594
|
`place({ locators })`. The fake has no AAA: outside a queue, `place` needs `locators`.
|
|
595
|
+
- `queues['S']` is the message queue: each item is a message's text (`string[]`), not a PNR.
|
|
596
|
+
`access` shows it without `Line`, as Sabre does, and `fake.messages()` lists the ones left.
|
|
597
|
+
That `remove` takes a message out is an assumption of the fake, not tested against Sabre.
|
|
598
|
+
- `queue.activity`: `numMessages` comes from the fake's queues (without the PNR on screen, as in
|
|
599
|
+
`count`, as CERT does). The other seven counters are 0 unless you seed them with
|
|
600
|
+
`activity: { '9': { ignored: 27 } }`. The fake's actions do **not** change them: no one has
|
|
601
|
+
tested which counter Sabre raises for each action.
|
|
602
|
+
- `reservation.history`: `histories: { AAAAAA: [transactions] }` sets what each PNR returns, in
|
|
603
|
+
the shape of `ReservationHistoryTransaction` (missing lists come back empty). A PNR the fake
|
|
604
|
+
knows without a seeded history returns one synthetic transaction (its creation), and an unknown
|
|
605
|
+
locator gives a host error marked as not simulated. Inside a queue, the history of another PNR
|
|
606
|
+
is rejected with `NAK6 -NOT ALLOWED- FINISH OR IGNORE OTHER ACTIVITY` and the one on screen is
|
|
607
|
+
returned, as in CERT.
|
|
499
608
|
- It answers like production: empty queues, the end of the queue, `place` moving the PNR, the
|
|
500
609
|
required PIC, and an expired session with `fake.expireSessions()`. `fake.calls` lists what the
|
|
501
610
|
SDK asked for.
|
|
502
611
|
- **Not simulated**: PCCs (the queues are the same for any `pcc`) and `selection`, which gets an
|
|
503
|
-
error instead of the whole queue
|
|
612
|
+
error instead of the whole queue, in `access` and in `list`.
|
|
504
613
|
- `queueFixtures` holds the anonymized responses recorded in production (`walk`, `emptyQueue`,
|
|
505
|
-
`place`, …), as `{ operation, status, body }`.
|
|
614
|
+
`place`, …), as `{ operation, status, body }`. Each one has its own locator aliases (`walk`:
|
|
615
|
+
`AAAAAA`–`AAAAAD`, `place`: `AAAAAE`–`AAAAAF`, `placeWithoutInstructionCodeInside`:
|
|
616
|
+
`AAAAAG`–`AAAAAH`), and their signature lines keep their shape (`… <alias> H`).
|
|
617
|
+
- `queueFakePnrs(fixture)` turns a recording into the fake's PNRs: those of `list` first, in
|
|
618
|
+
order, with their `Line`, and the paragraph `access`, `ignore`, `remove` or `place` showed for
|
|
619
|
+
each one. You choose the queue, and you can mix fixtures:
|
|
620
|
+
|
|
621
|
+
```ts
|
|
622
|
+
const fake = createQueueFake({
|
|
623
|
+
queues: { '9': queueFakePnrs(queueFixtures.walk), '50': [] },
|
|
624
|
+
pnrs: queueFakePnrs(queueFixtures.place),
|
|
625
|
+
});
|
|
626
|
+
```
|
|
506
627
|
|
|
507
628
|
`examples/queue-review.mjs` is a full example, and its test runs it against the fake.
|
|
508
629
|
|
|
630
|
+
#### Anonymizing recorded responses
|
|
631
|
+
|
|
632
|
+
`createAnonymizer` applies the rule the SDK's fixtures were recorded with, so you can store
|
|
633
|
+
Sabre's responses (for example from `onRawResponse`) without passenger data:
|
|
634
|
+
|
|
635
|
+
```ts
|
|
636
|
+
import { createAnonymizer } from 'sabre-client/testing';
|
|
637
|
+
|
|
638
|
+
const anonymizer = createAnonymizer({ secrets: [pcc, username] });
|
|
639
|
+
const safe = anonymizer.anonymize(raw.body); // a whole SOAP envelope
|
|
640
|
+
anonymizer.maskParagraph(item.paragraph); // host-format lines
|
|
641
|
+
if (anonymizer.leaks(safe).length > 0) throw new Error('real data left');
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
- Each locator gets an alias (`AAAAAA`, `AAAAAB`…), the same one everywhere it appears, also in
|
|
645
|
+
later texts passed to the same anonymizer. `PassengerName` becomes `PASAJERO/UNO`, `Group`
|
|
646
|
+
becomes `GRUPO`, `AgentSine` becomes `AAA` (except `SABRE`), `PseudoCityCode` becomes `XXXX`,
|
|
647
|
+
`LNIATA` becomes `XXXXXX`, and each `secret` becomes `XXXX`.
|
|
648
|
+
- In host-format lines (`<Text>`, or `maskLine` and `maskParagraph`), only the lines of a
|
|
649
|
+
whitelist stay as they are (`isSafeHostLine`: empty lines, PIC notices like `011 SEE REMARKS`,
|
|
650
|
+
section titles, the end of the queue). In the others, letters become `X` and digits `9`. The
|
|
651
|
+
signature line keeps its shape, and its locator only if it is already an alias.
|
|
652
|
+
- `leaks(text)` lists the real data still in `text` (`'secreto'`, `'localizador'`, `'nombre'`,
|
|
653
|
+
`'línea del PNR'`, `'token'`): use it before writing a file.
|
|
654
|
+
- Use one anonymizer per recording. It is a rule, not a guarantee: check what you store.
|
|
655
|
+
|
|
509
656
|
## Error handling
|
|
510
657
|
|
|
511
658
|
Every error thrown by the SDK extends `SabreError`:
|
|
@@ -576,6 +723,39 @@ In `SabreLegacy`, `protocol` is `'soap'`, `body` is the whole SOAP envelope, and
|
|
|
576
723
|
> `body` is the raw response and **contains passenger data**. The SDK never sends it to the
|
|
577
724
|
> logger. Where you store it is up to you.
|
|
578
725
|
|
|
726
|
+
## Raw request
|
|
727
|
+
|
|
728
|
+
The same operations accept `onRawRequest`, next to `onRawResponse`. It receives what the SDK sent
|
|
729
|
+
over the network, for example to show the real request next to the response:
|
|
730
|
+
|
|
731
|
+
```ts
|
|
732
|
+
import type { SabreRawRequest } from 'sabre-client';
|
|
733
|
+
|
|
734
|
+
let sent: SabreRawRequest | undefined;
|
|
735
|
+
await sabre.booking.get({ confirmationId }, { onRawRequest: (r) => (sent = r) });
|
|
736
|
+
// sent: { operation: 'booking.get', protocol: 'rest', method, url, headers, body, attempts }
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
- `operation` is the same value `onRawResponse` gets. `url` is the full URL, with its query
|
|
740
|
+
string. `headers` are the ones sent, **without `Authorization`** (the whole header is dropped).
|
|
741
|
+
`body` is the text sent, or `undefined` if there was none. In REST it is untouched; in
|
|
742
|
+
`SabreLegacy` it is the whole SOAP envelope with the `BinarySecurityToken` replaced by
|
|
743
|
+
`[REDACTED]`, as in the response.
|
|
744
|
+
- It is called **once**, with the last attempt sent, before the operation returns or throws and
|
|
745
|
+
before `onRawResponse`. Unlike `onRawResponse`, it is also called when no attempt got an HTTP
|
|
746
|
+
response (network error, abort), as long as at least one was sent. It is not called if the
|
|
747
|
+
operation throws before sending anything (for example a `TypeError` from validation).
|
|
748
|
+
- In SOAP, `eb:MessageId` and `eb:Timestamp` are new on every attempt, so the envelope you get is
|
|
749
|
+
the one of the last attempt.
|
|
750
|
+
- It is not called for authentication: the OAuth token request, `openSession`
|
|
751
|
+
(`SessionCreateRQ`, whose envelope carries the EPR password) and `close` (`SessionCloseRQ`).
|
|
752
|
+
- It is synchronous and its return value is ignored. If it throws, the SDK logs
|
|
753
|
+
`sabre.request.rawRequestHookFailed` without the body, and the operation carries on.
|
|
754
|
+
|
|
755
|
+
> [!WARNING]
|
|
756
|
+
> `body` and `url` **contain passenger data** (locators, names, contact details). The SDK never
|
|
757
|
+
> sends them to the logger. Where you store them is up to you.
|
|
758
|
+
|
|
579
759
|
## Retries
|
|
580
760
|
|
|
581
761
|
Only idempotent operations are retried: Bargain Finder Max and Revalidate Itinerary (both
|
|
@@ -632,7 +812,8 @@ const sabre = new Sabre({
|
|
|
632
812
|
```
|
|
633
813
|
|
|
634
814
|
The SDK logs structured events (`sabre.request.start`, `sabre.request.end`,
|
|
635
|
-
`sabre.request.retry`, `sabre.request.rawResponseHookFailed`,
|
|
815
|
+
`sabre.request.retry`, `sabre.request.rawResponseHookFailed`,
|
|
816
|
+
`sabre.request.rawRequestHookFailed`, `sabre.auth.token.refresh`,
|
|
636
817
|
`sabre.auth.token.store`, `sabre.booking.seat.priced`, `sabre.session.open`,
|
|
637
818
|
`sabre.session.close`, `sabre.session.closeFailed`, `sabre.session.keepAliveFailed`,
|
|
638
819
|
`sabre.session.command`, `sabre.queue.exitTolerated`, `sabre.queue.exitFailed`), never raw
|