Skip to content

Commit cbe2bab

Browse files
author
Igo Lapa
authored
feat(booking): add order update operation (#96)
Users can now update a single order by passing order id and metadata
1 parent 112cdbc commit cbe2bab

6 files changed

Lines changed: 101 additions & 4 deletions

File tree

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ module.exports = {
4444
'ord_00009hthhsUZ8W4LxQgkjo',
4545
'ser_00009UhD4ongolulWd9123',
4646
'seg_00009hj8USM7Ncg31cB456',
47+
'pit_00009htYpSCXrwaB9DnUm2',
4748
'Earhart',
4849
'Embraer',
4950
'errorMsg',

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,30 @@ const orderCancellationResponse = await duffel.orderCancellations.confirm(order_
217217
console.log(orderCancellationResponse.data.id)
218218
```
219219

220+
### Update
221+
222+
On certain endpoints you can perform updates, such as updating an order. Usually you'll do that by passing the object ID with the object data changes.
223+
224+
```javascript
225+
const orderUpdateResponse = await duffel.orders.update('ord_00009hthhsUZ8W4LxQgkjo', {
226+
metadata: {
227+
payment_intent_id: 'pit_00009htYpSCXrwaB9DnUm2'
228+
}
229+
})
230+
231+
console.log(orderUpdateResponse.data.id)
232+
```
233+
234+
And if you want to clear metadata:
235+
236+
```javascript
237+
const orderUpdateResponse = await duffel.orders.update('ord_00009hthhsUZ8W4LxQgkjo', {
238+
metadata: {}
239+
})
240+
241+
console.log(orderUpdateResponse.data.id)
242+
```
243+
220244
## Configuration
221245

222246
### Test and live modes

src/booking/Orders/Orders.spec.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ describe('Orders', () => {
1717

1818
test('should get a page of orders', async () => {
1919
nock(/(.*)/)
20-
.get(`/air/orders?limit=1`)
20+
.get(`/air/orders`)
21+
.query((queryObject) => {
22+
expect(queryObject.limit).toEqual('1')
23+
return true
24+
})
2125
.reply(200, { data: [mockOrder], meta: { limit: 1, before: null, after: null } })
2226

2327
const response = await new Orders(new Client({ token: 'mockToken' })).list({ limit: 1 })
@@ -38,7 +42,11 @@ describe('Orders', () => {
3842

3943
test('should get only on hold orders', async () => {
4044
nock(/(.*)/)
41-
.get(`/air/orders?awaiting_payment=true`)
45+
.get(`/air/orders`)
46+
.query((queryObject) => {
47+
expect(queryObject['awaiting_payment']).toEqual('true')
48+
return true
49+
})
4250
.reply(200, { data: mockOnHoldOrders, meta: { limit: 1, before: null, after: null } })
4351

4452
const response = await new Orders(new Client({ token: 'mockToken' })).list({ awaiting_payment: true })
@@ -48,7 +56,12 @@ describe('Orders', () => {
4856
})
4957

5058
test('should create an order', async () => {
51-
nock(/(.*)/).post(`/air/orders`).query(true).reply(200, { data: mockOrder })
59+
nock(/(.*)/)
60+
.post(`/air/orders`, (body) => {
61+
expect(body.data).toEqual(mockCreateOrderRequest)
62+
return true
63+
})
64+
.reply(200, { data: mockOrder })
5265

5366
const response = await new Orders(new Client({ token: 'mockToken' })).create(mockCreateOrderRequest)
5467
expect(response.data?.id).toBe(mockOrder.id)
@@ -73,4 +86,30 @@ describe('Orders', () => {
7386
expect(response.data).toHaveLength(1)
7487
expect(response.data[0].booking_reference).toBe('RZPNX8')
7588
})
89+
90+
test('should update a single order', async () => {
91+
const metadata = { payment_intent_id: 'pit_00009htYpSCXrwaB9DnUm2' }
92+
nock(/(.*)/)
93+
.patch(`/air/orders/${mockOrder.id}`, (body) => {
94+
expect(body.data.options.metadata['payment_intent_id']).toEqual(metadata['payment_intent_id'])
95+
return true
96+
})
97+
.reply(200, { data: mockOrder })
98+
99+
const response = await new Orders(new Client({ token: 'mockToken' })).update(mockOrder.id, { metadata })
100+
expect(response.data?.id).toBe(mockOrder.id)
101+
})
102+
103+
test('should update a single order and clear metadata', async () => {
104+
const metadata = {}
105+
nock(/(.*)/)
106+
.patch(`/air/orders/${mockOrder.id}`, (body) => {
107+
expect(body.data.options.metadata).toEqual(metadata)
108+
return true
109+
})
110+
.reply(200, { data: mockOrder })
111+
112+
const response = await new Orders(new Client({ token: 'mockToken' })).update(mockOrder.id, { metadata })
113+
expect(response.data?.id).toBe(mockOrder.id)
114+
})
76115
})

src/booking/Orders/Orders.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Resource } from '../../Resource'
2-
import { CreateOrder, DuffelResponse, ListParamsOrders, Order, PaginationMeta } from '../../types'
2+
import { CreateOrder, DuffelResponse, ListParamsOrders, Order, PaginationMeta, UpdateSingleOrder } from '../../types'
33

44
export class Orders extends Resource {
55
/**
@@ -42,4 +42,16 @@ export class Orders extends Resource {
4242
public create = async (options: CreateOrder): Promise<DuffelResponse<Order>> => {
4343
return this.request({ method: 'POST', path: this.path, data: options })
4444
}
45+
46+
/**
47+
* Updates a single order
48+
* @description Some order fields are updateable. Each field that can be updated is detailed in the request object.
49+
* @param {string} id - Duffel's unique identifier for the order
50+
* @param {Object.UpdateSingleOrder} options
51+
* @example (id: 'ord_00009hthhsUZ8W4LxQgkjo', { metadata: { 'payment_intent_id': 'pit_00009htYpSCXrwaB9DnUm2' } } )
52+
* @link https://duffel.com/docs/api/orders/update-order-by-id
53+
*/
54+
public update = async (id: string, options: UpdateSingleOrder): Promise<DuffelResponse<Order>> => {
55+
return this.request({ method: 'PATCH', path: `${this.path}/${id}`, data: { options } })
56+
}
4557
}

src/booking/Orders/OrdersTypes.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,14 @@ export interface Order {
490490
* In some cases, you may need to contact the Duffel support team or the airline directly.
491491
*/
492492
conditions: FlightsConditions
493+
494+
/**
495+
* Metadata contains a set of key-value pairs that you can attach to an object.
496+
* It can be useful for storing additional information about the object, in a structured format.
497+
* Duffel does not use this information.
498+
* You should not store sensitive information in this field.
499+
*/
500+
metadata: Record<string, string>
493501
}
494502

495503
export interface CreateOrder {
@@ -535,3 +543,7 @@ export interface ListParamsOrders {
535543
*/
536544
booking_reference?: string
537545
}
546+
547+
export interface UpdateSingleOrder {
548+
metadata: Order['metadata']
549+
}

src/booking/Orders/mockOrders.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ export const mockOrder: Order = {
213213
id: 'aln_00001876aqC8c5umZmrRds',
214214
iata_code: 'BA'
215215
},
216+
metadata: {
217+
customer_prefs: 'window seat'
218+
},
216219
live_mode: false,
217220
id: 'ord_00009hthhsUZ8W4LxQgkjo',
218221
documents: [
@@ -384,6 +387,9 @@ export const mockOnHoldOrders: Order[] = [
384387
id: 'arl_00009VME7DAGiJjwomhv32',
385388
iata_code: 'AA'
386389
},
390+
metadata: {
391+
customer_prefs: 'window seat'
392+
},
387393
live_mode: false,
388394
id: 'ord_0000A6GioOO1UDbjb7nIi8',
389395
documents: [],
@@ -540,6 +546,9 @@ export const mockOnHoldOrders: Order[] = [
540546
id: 'arl_00009VME7DAGiJjwomhv32',
541547
iata_code: 'AA'
542548
},
549+
metadata: {
550+
customer_prefs: 'window seat'
551+
},
543552
live_mode: false,
544553
id: 'ord_0000A6GiZRU4WXtdZJrivT',
545554
documents: [],

0 commit comments

Comments
 (0)