Skip to content

Commit af15c66

Browse files
authored
Merge pull request #1131 from duffelhq/thep/grnd-249-add-cars-api-to-javascript-sdk
Add Cars rental API to the SDK
2 parents 1f6855c + f7a7b1a commit af15c66

12 files changed

Lines changed: 719 additions & 0 deletions

File tree

src/Cars/Bookings/Bookings.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import nock from 'nock'
2+
import { Duffel } from '../../index'
3+
import { MOCK_BOOKING, MOCK_CREATE_BOOKING_PAYLOAD } from '../mocks'
4+
5+
const duffel = new Duffel({ token: 'mockToken' })
6+
describe('Cars/Bookings', () => {
7+
afterEach(() => {
8+
nock.cleanAll()
9+
})
10+
11+
it('should post to /cars/bookings when `create` is called', async () => {
12+
const mockResponse = { data: MOCK_BOOKING }
13+
14+
nock(/(.*)/)
15+
.post('/cars/bookings', (body) => {
16+
expect(body.data).toEqual(MOCK_CREATE_BOOKING_PAYLOAD)
17+
return true
18+
})
19+
.reply(200, mockResponse)
20+
21+
const response = await duffel.cars.bookings.create(
22+
MOCK_CREATE_BOOKING_PAYLOAD,
23+
)
24+
expect(response.data).toEqual(mockResponse.data)
25+
})
26+
27+
it('should get /cars/bookings/{id} when `get` is called', async () => {
28+
const bookingId = 'boo_0000Cx4Af0b5l45AT50eqO'
29+
const mockResponse = { data: MOCK_BOOKING }
30+
31+
nock(/(.*)/).get(`/cars/bookings/${bookingId}`).reply(200, mockResponse)
32+
33+
const response = await duffel.cars.bookings.get(bookingId)
34+
expect(response.data).toEqual(mockResponse.data)
35+
})
36+
37+
it('should post to /cars/bookings/{id}/actions/cancel when `cancel` is called', async () => {
38+
const bookingId = 'boo_0000Cx4Af0b5l45AT50eqO'
39+
const mockResponse = {
40+
data: {
41+
...MOCK_BOOKING,
42+
status: 'cancelled',
43+
cancelled_at: '2024-01-16T10:00:00Z',
44+
},
45+
}
46+
47+
nock(/(.*)/)
48+
.post(`/cars/bookings/${bookingId}/actions/cancel`)
49+
.reply(200, mockResponse)
50+
51+
const response = await duffel.cars.bookings.cancel(bookingId)
52+
expect(response.data).toEqual(mockResponse.data)
53+
})
54+
})

src/Cars/Bookings/Bookings.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Client } from '../../Client'
2+
import { CarsBooking } from '../CarsTypes'
3+
import { Resource } from '../../Resource'
4+
import { DuffelResponse } from '../../types'
5+
6+
export interface CarsBookingPayload {
7+
quote_id: string
8+
driver: {
9+
given_name: string
10+
family_name: string
11+
email: string
12+
phone_number: string
13+
date_of_birth: string
14+
user_id?: string
15+
}
16+
inbound_flight_number?: string
17+
supplier_loyalty_programme_account_number?: string
18+
payment?: {
19+
method: 'card'
20+
card_id: string
21+
}
22+
metadata?: Record<string, string>
23+
users?: string[]
24+
}
25+
26+
export class Bookings extends Resource {
27+
/**
28+
* Endpoint path
29+
*/
30+
path: string
31+
32+
constructor(client: Client) {
33+
super(client)
34+
this.path = 'cars/bookings'
35+
}
36+
37+
/**
38+
* Create a booking from a quote
39+
* @param {object} payload - The booking payload, including quote id and driver information
40+
*/
41+
public create = async (
42+
payload: CarsBookingPayload,
43+
): Promise<DuffelResponse<CarsBooking>> =>
44+
this.request({
45+
method: 'POST',
46+
path: this.path,
47+
data: payload,
48+
})
49+
50+
/**
51+
* Get a booking by ID
52+
* @param {string} bookingId - The ID of the booking
53+
*/
54+
public get = async (
55+
bookingId: string,
56+
): Promise<DuffelResponse<CarsBooking>> =>
57+
this.request({
58+
method: 'GET',
59+
path: `${this.path}/${bookingId}`,
60+
})
61+
62+
/**
63+
* Cancel a booking
64+
* @param {string} bookingId - The ID of the booking to cancel
65+
*/
66+
public cancel = async (
67+
bookingId: string,
68+
): Promise<DuffelResponse<CarsBooking>> =>
69+
this.request({
70+
method: 'POST',
71+
path: `${this.path}/${bookingId}/actions/cancel`,
72+
})
73+
}

src/Cars/Bookings/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Bookings'

src/Cars/Cars.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import nock from 'nock'
2+
import { Duffel } from '../index'
3+
import { MOCK_SEARCH, MOCK_SEARCH_PARAMS } from './mocks'
4+
5+
const duffel = new Duffel({ token: 'mockToken' })
6+
describe('Cars', () => {
7+
afterEach(() => {
8+
nock.cleanAll()
9+
})
10+
11+
it('should post to /cars/search when `search` is called', async () => {
12+
const mockResponse = { data: MOCK_SEARCH }
13+
14+
nock(/(.*)/)
15+
.post('/cars/search', (body) => {
16+
expect(body.data).toEqual(MOCK_SEARCH_PARAMS)
17+
return true
18+
})
19+
.reply(200, mockResponse)
20+
21+
const response = await duffel.cars.search(MOCK_SEARCH_PARAMS)
22+
expect(response.data).toEqual(mockResponse.data)
23+
})
24+
})

src/Cars/Cars.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Client } from '../Client'
2+
import { CarsSearchParams, CarsSearch } from './CarsTypes'
3+
import { Resource } from '../Resource'
4+
import { DuffelResponse } from '../types'
5+
import { Bookings } from './Bookings'
6+
import { Quotes } from './Quotes'
7+
8+
export class Cars extends Resource {
9+
/**
10+
* Endpoint path
11+
*/
12+
path: string
13+
14+
public bookings: Bookings
15+
public quotes: Quotes
16+
17+
constructor(client: Client) {
18+
super(client)
19+
this.path = 'cars'
20+
21+
this.bookings = new Bookings(client)
22+
this.quotes = new Quotes(client)
23+
}
24+
25+
/**
26+
* Search for available rental cars
27+
* @param {object} params - The search parameters
28+
*/
29+
public search = async (
30+
params: CarsSearchParams,
31+
): Promise<DuffelResponse<CarsSearch>> =>
32+
this.request({ method: 'POST', path: `${this.path}/search`, data: params })
33+
}

0 commit comments

Comments
 (0)