Skip to content

Commit aa38939

Browse files
authored
Merge pull request #1066 from duffelhq/add-batch-offer-request
feat: add batch offer requests
2 parents eec6014 + 3792ca9 commit aa38939

File tree

9 files changed

+259
-1
lines changed

9 files changed

+259
-1
lines changed

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
nodejs 18.20.8
1+
nodejs 18.20.8
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import nock from 'nock'
2+
import { Client } from '../../Client'
3+
import {
4+
mockCreateBatchOfferRequest,
5+
mockBatchOfferRequest,
6+
} from './mockBatchOfferRequest'
7+
import { BatchOfferRequests } from './BatchOfferRequests'
8+
9+
describe('BatchOfferRequests', () => {
10+
afterEach(() => {
11+
nock.cleanAll()
12+
})
13+
14+
test('should get a single offer request', async () => {
15+
nock(/(.*)/)
16+
.get(`/air/batch_offer_requests/${mockBatchOfferRequest.id}`)
17+
.reply(200, { data: mockBatchOfferRequest })
18+
19+
const response = await new BatchOfferRequests(
20+
new Client({ token: 'mockToken' }),
21+
).get(mockBatchOfferRequest.id)
22+
expect(response.data?.id).toBe(mockBatchOfferRequest.id)
23+
})
24+
25+
test('should create an offer request and returns the id', async () => {
26+
nock(/(.*)/)
27+
.post(`/air/batch_offer_requests/`)
28+
.reply(200, { data: mockBatchOfferRequest })
29+
30+
const response = await new BatchOfferRequests(
31+
new Client({ token: 'mockToken' }),
32+
).create(mockCreateBatchOfferRequest)
33+
expect(response.data?.id).toBe(mockBatchOfferRequest.id)
34+
})
35+
})
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Client } from '../../Client'
2+
import { Resource } from '../../Resource'
3+
import {
4+
BatchOfferRequest,
5+
CreateBatchOfferRequest,
6+
CreateBatchOfferRequestResponse,
7+
CreateBatchOfferRequestQueryParameters,
8+
DuffelResponse,
9+
} from '../../types'
10+
11+
/**
12+
* To search for flights, you'll need to create an `offer request`.
13+
* The batch offer requests endpoint allows you to retrieve orders as soon as they become available.
14+
* They function as long-polling resources that you can repeatedly retrieve after creating them, returning whatever offers are available at the time or waiting for more to become available. Batches are expected to be consumed promptly, and as such, batch offer requests expire after one minute. However, offers remain accessible and can be retrieved using the offer request ID as usual.
15+
* @class
16+
* @link https://duffel.com/docs/api/batch-offer-requests
17+
*/
18+
export class BatchOfferRequests extends Resource {
19+
/**
20+
* Endpoint path
21+
*/
22+
path: string
23+
24+
constructor(client: Client) {
25+
super(client)
26+
this.path = 'air/batch_offer_requests'
27+
}
28+
29+
/**
30+
* Call this endpoint repeatedly to retrieve all the offers as they become available. The total_batches and remaining_batches properties can be used to estimate the remaining amount of work, although you may receive multiple batches at the same time if multiple batches are available.
31+
* Once you get a response with remaining_batches of 0 you can stop requesting the endpoint as there are no more offers coming.
32+
* @param {string} id - Duffel's unique identifier for the offer request
33+
* @link https:/duffel.com/docs/api/offer-requests/get-offer-request-by-id
34+
*/
35+
public get = async (id: string): Promise<DuffelResponse<BatchOfferRequest>> =>
36+
this.request({ method: 'GET', path: `${this.path}/${id}` })
37+
38+
/**
39+
* To search for flights, you'll need to create an `offer request`.
40+
* An offer request describes the passengers and where and when they want to travel (in the form of a list of `slices`).
41+
* It may also include additional filters (e.g. a particular cabin to travel in).
42+
* Batch offer requests are a mechanism for retrieving offers as they become available, instead of waiting for the entire offer payload to finish processing.
43+
* They function as long-polling resources that you can repeatedly retrieve after creating them, returning whatever offers are available at the time or waiting for more to become available. Batches are expected to be consumed promptly, and as such, batch offer requests expire after one minute.
44+
* However, offers remain accessible and can be retrieved using the offer request ID as usual.
45+
* @param {Object} [options] - the parameters for making an offer requests (required: slices, passengers; optional: cabin_class)
46+
* @link https://duffel.com/docs/api/v2/batch-offer-requests/create-batch-offer-request
47+
*/
48+
public create = async <
49+
QueryParams extends CreateBatchOfferRequestQueryParameters,
50+
>(
51+
options: CreateBatchOfferRequest & QueryParams,
52+
): Promise<DuffelResponse<CreateBatchOfferRequestResponse>> => {
53+
const { supplier_timeout, ...data } = options
54+
55+
return this.request({
56+
method: 'POST',
57+
path: `${this.path}/`,
58+
data,
59+
params: {
60+
...(supplier_timeout !== undefined &&
61+
supplier_timeout !== null && { supplier_timeout }),
62+
},
63+
})
64+
}
65+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { CabinClass, Offer } from '../../types'
2+
3+
import {
4+
CreateOfferRequestPassenger,
5+
CreateOfferRequestSlice,
6+
CreateOfferRequestPrivateFare,
7+
} from '../OfferRequests/OfferRequestsTypes'
8+
9+
export interface BatchOfferRequest {
10+
/**
11+
* The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime at which the offer request was created
12+
*/
13+
created_at: string
14+
15+
/**
16+
* Duffel's unique identifier for the offer request
17+
*/
18+
id: string
19+
20+
/**
21+
* Whether the offer request was created in live mode. This field will be set to true if the offer request was created in live mode, or false if it was created in test mode.
22+
*/
23+
live_mode: boolean
24+
25+
/**
26+
* A client key to allow the Duffel Ancillaries component to talk to the Duffel API to retrieve information about an offer and its ancillaries. Learn more about how to use this on https://duffel.com/docs/guides/ancillaries-component.
27+
*/
28+
client_key: string
29+
30+
/**
31+
* The total number of batches of offers.
32+
*/
33+
total_batches: number
34+
35+
/**
36+
* The number of batches of offers that are remaining. This can be used with the total_batches to estimate the amount of work remaining. Once this reaches zero, there are no more batches of offers to process.
37+
*/
38+
remaining_batches: number
39+
40+
/**
41+
* The offers related to this batch offer request.
42+
*/
43+
offers: Omit<Offer, 'available_services'>[]
44+
}
45+
46+
export type CreateBatchOfferRequestResponse = Omit<BatchOfferRequest, 'offers'>
47+
48+
export interface CreateBatchOfferRequest {
49+
/**
50+
* The cabin that the passengers want to travel in.
51+
*/
52+
cabin_class?: CabinClass
53+
54+
/**
55+
* The maximum number of connections within any slice of the offer. For
56+
* example 0 means a direct flight which will have a single segment within
57+
* each slice and 1 means a maximum of two segments within each slice of the
58+
* offer.
59+
*/
60+
max_connections?: 0 | 1 | 2
61+
62+
/**
63+
* The passengers who want to travel. If you specify an `age` for a passenger,
64+
* the `type` may differ for the same passenger in different offers due to
65+
* airline's different rules. E.g. one airline may treat a 14 year old as an
66+
* adult, and another as a young adult. You may only specify an `age` or a
67+
* `type` – not both.
68+
*/
69+
passengers: CreateOfferRequestPassenger[]
70+
71+
/**
72+
* The private fare codes for this Offer Request. You can pass in multiple
73+
* airlines with their specific private fare codes. The key is the airline's
74+
* IATA code that provided the private fare code. The `corporate_code` is
75+
* provided to you by the airline and the `tracking_reference` is to identify
76+
* your business by the airlines.
77+
*/
78+
private_fares?: {
79+
[iataCode: string]: CreateOfferRequestPrivateFare[]
80+
}
81+
82+
/**
83+
* The [slices](https://duffel.com/docs/api/overview/key-principles) that make
84+
* up this offer request. One-way journeys can be expressed using one slice,
85+
* whereas return trips will need two.
86+
*/
87+
slices: CreateOfferRequestSlice[]
88+
}
89+
90+
export interface CreateBatchOfferRequestQueryParameters {
91+
/**
92+
* The maximum amount of time in milliseconds to wait for each airline search to complete.
93+
* This timeout applies to the response time of the call to the airline and includes
94+
* some additional overhead added by Duffel. Value should be between `2` seconds and `60` seconds.
95+
* Any values outside the range will be ignored and the default supplier_timeout will be used.
96+
* If a value is set, the response will only include offers from airline searches that completed
97+
* within the given time. If a value is not set, the response will only include offers from
98+
* airline searches that completed within the default supplier_timeout value of 20 seconds.
99+
* We recommend setting supplier_timeout lower than the timeout on the HTTP request you send to
100+
* Duffel API as that will allow us to respond with the offers we received before your request
101+
* times out with an empty response.
102+
*/
103+
supplier_timeout?: number
104+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './BatchOfferRequests'
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { mockOffer } from '../Offers/mockOffer'
2+
import {
3+
CreateBatchOfferRequest,
4+
CreateBatchOfferRequestResponse,
5+
BatchOfferRequest,
6+
} from '../../types'
7+
8+
export const mockCreateBatchOfferRequest: CreateBatchOfferRequest = {
9+
slices: [
10+
{
11+
origin: 'LHR',
12+
destination: 'JFK',
13+
departure_date: '2020-04-24',
14+
arrival_time: null,
15+
departure_time: null,
16+
},
17+
],
18+
passengers: [
19+
{
20+
type: 'adult',
21+
},
22+
{
23+
age: 14,
24+
},
25+
],
26+
cabin_class: 'economy',
27+
max_connections: 1,
28+
}
29+
30+
export const mockBatchOfferRequest: BatchOfferRequest = {
31+
total_batches: 2,
32+
remaining_batches: 1,
33+
offers: [mockOffer],
34+
live_mode: false,
35+
id: 'orq_00009hjdomFOCJyxHG7k7k',
36+
created_at: '2020-02-12T15:21:01.927Z',
37+
client_key: 'example_client_key',
38+
}
39+
40+
export const mocCreatedBatchOfferRequest: CreateBatchOfferRequestResponse = {
41+
total_batches: 2,
42+
remaining_batches: 2,
43+
live_mode: false,
44+
id: 'orq_00009hjdomFOCJyxHG7k7k',
45+
created_at: '2020-02-12T15:21:01.927Z',
46+
client_key: 'example_client_key',
47+
}

src/booking/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './AirlineInitiatedChanges'
22
export * from './Identity'
3+
export * from './BatchOfferRequests'
34
export * from './OfferRequests'
45
export * from './Offers'
56
export * from './OrderCancellations'

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { PaymentIntents } from './DuffelPayments'
22
import {
3+
BatchOfferRequests,
34
OfferRequests,
45
Offers,
56
OrderCancellations,
@@ -29,6 +30,7 @@ export interface DuffelAPIClient {
2930
aircraft: Aircraft
3031
airlines: Airlines
3132
airports: Airports
33+
batchOfferRequests: BatchOfferRequests
3234
offers: Offers
3335
offerRequests: OfferRequests
3436
orders: Orders
@@ -48,6 +50,7 @@ export class Duffel {
4850
public airlines: Airlines
4951
public airports: Airports
5052
public links: Sessions
53+
public batchOfferRequests: BatchOfferRequests
5154
public offerRequests: OfferRequests
5255
public offers: Offers
5356
public orders: Orders
@@ -77,6 +80,7 @@ export class Duffel {
7780
this.airports = new Airports(this.client)
7881
this.airlineInitiatedChanges = new AirlineInitiatedChanges(this.client)
7982
this.links = new Sessions(this.client)
83+
this.batchOfferRequests = new BatchOfferRequests(this.client)
8084
this.offerRequests = new OfferRequests(this.client)
8185
this.offers = new Offers(this.client)
8286
this.orders = new Orders(this.client)

src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from '../Places/Suggestions/SuggestionsType'
33
export * from '../Stays/StaysTypes'
44
export * from '../booking/AirlineInitiatedChanges/AirlineInitiatedChangesTypes'
55
export * from '../booking/OfferRequests/OfferRequestsTypes'
6+
export * from '../booking/BatchOfferRequests/BatchOfferRequestsTypes'
67
export * from '../booking/Offers/OfferTypes'
78
export * from '../booking/OrderCancellations/OrderCancellationsTypes'
89
export * from '../booking/OrderChangeOffers/OrderChangeOfferTypes'

0 commit comments

Comments
 (0)