diff --git a/duffel_api/api/__init__.py b/duffel_api/api/__init__.py index 4909e80f..876d2bb9 100644 --- a/duffel_api/api/__init__.py +++ b/duffel_api/api/__init__.py @@ -5,7 +5,7 @@ from .booking.offers import OfferClient from .booking.orders import OrderClient, OrderCreate, OrderUpdate from .booking.order_cancellations import OrderCancellationClient -from .booking.order_change_offers import OrderChangeOffersClient +from .booking.order_change_offers import OrderChangeOfferClient from .booking.payments import PaymentClient from .booking.seat_maps import SeatMapClient from .duffel_payments.payment_intents import PaymentIntentClient, PaymentIntentCreate @@ -18,7 +18,7 @@ OfferRequestClient, OfferRequestCreate, OfferClient, - OrderChangeOffersClient, + OrderChangeOfferClient, OrderClient, OrderCreate, OrderUpdate, diff --git a/duffel_api/api/booking/order_change_offers.py b/duffel_api/api/booking/order_change_offers.py index b1edb239..920f9704 100644 --- a/duffel_api/api/booking/order_change_offers.py +++ b/duffel_api/api/booking/order_change_offers.py @@ -2,7 +2,7 @@ from ...models import OrderChangeOffer -class OrderChangeOffersClient(HttpClient): +class OrderChangeOfferClient(HttpClient): """Client to interact with Order Change Offers""" def __init__(self, **kwargs): diff --git a/duffel_api/client.py b/duffel_api/client.py index 4b46db4e..fdf98867 100644 --- a/duffel_api/client.py +++ b/duffel_api/client.py @@ -7,6 +7,7 @@ OfferClient, OrderCancellationClient, OrderClient, + OrderChangeOfferClient, PaymentClient, PaymentIntentClient, SeatMapClient, @@ -36,6 +37,7 @@ def __init__(self, **kwargs): self.payment_client = None self.seat_map_client = None self.webhook_client = None + self.order_change_offer_client = None @property def aircraft(self): @@ -94,6 +96,17 @@ def order_cancellations(self): ) return self.order_cancellation_client + @property + def order_change_offers(self): + """Order Change Offers API - /air/order_change_offers""" + if isinstance(self.order_change_offer_client, type(None)): + setattr( + self, + "order_change_offer_client", + OrderChangeOfferClient(**self._kwargs), + ) + return self.order_change_offer_client + @property def payment_intents(self): """Payment Intents API - /payments/payment_intents""" diff --git a/tests/fixtures/get-order-change-offer-by-id.json b/tests/fixtures/get-order-change-offer-by-id.json new file mode 100644 index 00000000..3b5e806c --- /dev/null +++ b/tests/fixtures/get-order-change-offer-by-id.json @@ -0,0 +1,310 @@ +{ + "data": { + "change_total_amount": "90.80", + "change_total_currency": "GBP", + "created_at": "2020-01-17T10:12:14.545Z", + "expires_at": "2020-01-17T10:42:14.545Z", + "id": "oco_0000A3vUda8dKRtUSQPSXw", + "new_total_amount": "35.50", + "new_total_currency": "GBP", + "order_change_id": "oce_0000A4QasEUIjJ6jHKfhHU", + "penalty_amount": "10.50", + "penalty_currency": "GBP", + "refund_to": "arc_bsp_cash", + "slices": { + "add": [ + { + "destination": { + "airports": [ + { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + } + ], + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_city_code": "NYC", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York", + "type": "airport" + }, + "destination_type": "airport", + "duration": "PT02H26M", + "id": "sli_00009htYpSCXrwaB9Dn123", + "origin": { + "airports": [ + { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + } + ], + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_city_code": "LON", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London", + "type": "airport" + }, + "origin_type": "airport", + "segments": [ + { + "aircraft": { + "iata_code": "380", + "id": "arc_00009UhD4ongolulWd91Ky", + "name": "Airbus Industries A380" + }, + "arriving_at": "2020-06-13T16:38:02", + "departing_at": "2020-06-13T16:38:02", + "destination": { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + }, + "destination_terminal": "5", + "distance": "424.2", + "duration": "PT02H26M", + "id": "seg_00009htYpSCXrwaB9Dn456", + "marketing_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "marketing_carrier_flight_number": "1234", + "operating_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "operating_carrier_flight_number": "4321", + "origin": { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + }, + "origin_terminal": "B" + } + ] + } + ], + "remove": [ + { + "destination": { + "airports": [ + { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + } + ], + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_city_code": "NYC", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York", + "type": "airport" + }, + "destination_type": "airport", + "duration": "PT02H26M", + "id": "sli_00009htYpSCXrwaB9Dn123", + "origin": { + "airports": [ + { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + } + ], + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_city_code": "LON", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London", + "type": "airport" + }, + "origin_type": "airport", + "segments": [ + { + "aircraft": { + "iata_code": "380", + "id": "arc_00009UhD4ongolulWd91Ky", + "name": "Airbus Industries A380" + }, + "arriving_at": "2020-06-13T16:38:02", + "departing_at": "2020-06-13T16:38:02", + "destination": { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + }, + "destination_terminal": "5", + "distance": "424.2", + "duration": "PT02H26M", + "id": "seg_00009htYpSCXrwaB9Dn456", + "marketing_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "marketing_carrier_flight_number": "1234", + "operating_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "operating_carrier_flight_number": "4321", + "origin": { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + }, + "origin_terminal": "B" + } + ] + } + ] + }, + "updated_at": "2020-01-17T10:12:14.545Z" + } +} diff --git a/tests/fixtures/get-order-change-offers-by-order-change-request-id.json b/tests/fixtures/get-order-change-offers-by-order-change-request-id.json new file mode 100644 index 00000000..04f6f9b4 --- /dev/null +++ b/tests/fixtures/get-order-change-offers-by-order-change-request-id.json @@ -0,0 +1,316 @@ +{ + "data": [ + { + "change_total_amount": "90.80", + "change_total_currency": "GBP", + "created_at": "2020-01-17T10:12:14.545Z", + "expires_at": "2020-01-17T10:42:14.545Z", + "id": "oco_0000A3vUda8dKRtUSQPSXw", + "new_total_amount": "35.50", + "new_total_currency": "GBP", + "order_change_id": "oce_0000A4QasEUIjJ6jHKfhHU", + "penalty_amount": "10.50", + "penalty_currency": "GBP", + "refund_to": "arc_bsp_cash", + "slices": { + "add": [ + { + "destination": { + "airports": [ + { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + } + ], + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_city_code": "NYC", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York", + "type": "airport" + }, + "destination_type": "airport", + "duration": "PT02H26M", + "id": "sli_00009htYpSCXrwaB9Dn123", + "origin": { + "airports": [ + { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + } + ], + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_city_code": "LON", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London", + "type": "airport" + }, + "origin_type": "airport", + "segments": [ + { + "aircraft": { + "iata_code": "380", + "id": "arc_00009UhD4ongolulWd91Ky", + "name": "Airbus Industries A380" + }, + "arriving_at": "2020-06-13T16:38:02", + "departing_at": "2020-06-13T16:38:02", + "destination": { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + }, + "destination_terminal": "5", + "distance": "424.2", + "duration": "PT02H26M", + "id": "seg_00009htYpSCXrwaB9Dn456", + "marketing_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "marketing_carrier_flight_number": "1234", + "operating_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "operating_carrier_flight_number": "4321", + "origin": { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + }, + "origin_terminal": "B" + } + ] + } + ], + "remove": [ + { + "destination": { + "airports": [ + { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + } + ], + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_city_code": "NYC", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York", + "type": "airport" + }, + "destination_type": "airport", + "duration": "PT02H26M", + "id": "sli_00009htYpSCXrwaB9Dn123", + "origin": { + "airports": [ + { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + } + ], + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_city_code": "LON", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London", + "type": "airport" + }, + "origin_type": "airport", + "segments": [ + { + "aircraft": { + "iata_code": "380", + "id": "arc_00009UhD4ongolulWd91Ky", + "name": "Airbus Industries A380" + }, + "arriving_at": "2020-06-13T16:38:02", + "departing_at": "2020-06-13T16:38:02", + "destination": { + "city": { + "iata_code": "NYC", + "iata_country_code": "US", + "id": "cit_nyc_us", + "name": "New York" + }, + "city_name": "New York", + "iata_code": "JFK", + "iata_country_code": "US", + "icao_code": "KJFK", + "id": "arp_jfk_us", + "latitude": 40.640556, + "longitude": -73.778519, + "name": "John F. Kennedy International Airport", + "time_zone": "America/New_York" + }, + "destination_terminal": "5", + "distance": "424.2", + "duration": "PT02H26M", + "id": "seg_00009htYpSCXrwaB9Dn456", + "marketing_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "marketing_carrier_flight_number": "1234", + "operating_carrier": { + "iata_code": "BA", + "id": "aln_00001876aqC8c5umZmrRds", + "name": "British Airways" + }, + "operating_carrier_flight_number": "4321", + "origin": { + "city": { + "iata_code": "LON", + "iata_country_code": "GB", + "id": "cit_lon_gb", + "name": "London" + }, + "city_name": "London", + "iata_code": "LHR", + "iata_country_code": "GB", + "icao_code": "EGLL", + "id": "arp_lhr_gb", + "latitude": 64.068865, + "longitude": -141.951519, + "name": "Heathrow", + "time_zone": "Europe/London" + }, + "origin_terminal": "B" + } + ] + } + ] + }, + "updated_at": "2020-01-17T10:12:14.545Z" + } + ], + "meta": { + "after": "g2wAAAACbQAAABBBZXJvbWlzdC1LaGFya2l2bQAAAB=", + "limit": 50 + } +} diff --git a/tests/test_order_change_offers.py b/tests/test_order_change_offers.py new file mode 100644 index 00000000..ffd22996 --- /dev/null +++ b/tests/test_order_change_offers.py @@ -0,0 +1,65 @@ +import datetime + +from .fixtures import fixture + + +def test_get_order_change_offer_by_id(requests_mock): + url = "air/order_change_offers/id" + with fixture("get-order-change-offer-by-id", url, requests_mock.get, 200) as client: + order_change_offer = client.order_change_offers.get("id") + + assert order_change_offer.id == "oco_0000A3vUda8dKRtUSQPSXw" + assert order_change_offer.change_total_amount == "90.80" + assert order_change_offer.change_total_currency == "GBP" + assert order_change_offer.created_at == datetime.datetime( + 2020, 1, 17, 10, 12, 14, 545000 + ) + assert order_change_offer.expires_at == datetime.datetime( + 2020, 1, 17, 10, 42, 14, 545000 + ) + assert order_change_offer.id == "oco_0000A3vUda8dKRtUSQPSXw" + assert order_change_offer.new_total_amount == "35.50" + assert order_change_offer.new_total_currency == "GBP" + assert order_change_offer.order_change_id == "oce_0000A4QasEUIjJ6jHKfhHU" + assert order_change_offer.penalty_amount == "10.50" + assert order_change_offer.penalty_currency == "GBP" + assert order_change_offer.refund_to == "arc_bsp_cash" + assert order_change_offer.updated_at == datetime.datetime( + 2020, 1, 17, 10, 12, 14, 545000 + ) + assert isinstance(order_change_offer.slices.add, type([])) + assert isinstance(order_change_offer.slices.remove, type([])) + order_change_offer.slices.add[0].id == "sli_00009htYpSCXrwaB9Dn123" + order_change_offer.slices.remove[0].id == "sli_00009htYpSCXrwaB9Dn123" + + +def test_get_order_change_offers(requests_mock): + # We need a way to ensure pagination finished in a mocking environment + end_pagination_url = ( + "http://someaddress/air/order_change_offers?limit=50" + + "&order_change_offer_request_id=ocr_0000A3bQP9RLVfNUcdpLpw" + + "&after=g2wAAAACbQAAABBBZXJvbWlzdC1LaGFya2l2bQAAAB%3D" + ) + end_pagination_response = {"meta": {"after": None}, "data": []} + requests_mock.get( + end_pagination_url, complete_qs=True, json=end_pagination_response + ) + + url = ( + "http://someaddress/air/order_change_offers?limit=50" + + "&order_change_offer_request_id=ocr_0000A3bQP9RLVfNUcdpLpw" + ) + + with fixture( + "get-order-change-offers-by-order-change-request-id", + url, + requests_mock.get, + 200, + ) as client: + paginated_order_change_offers = client.order_change_offers.list( + "ocr_0000A3bQP9RLVfNUcdpLpw" + ) + order_change_offers = list(paginated_order_change_offers) + assert len(order_change_offers) == 1 + order_change_offer = order_change_offers[0] + assert order_change_offer.id == "off_00009htYpSCXrwaB9DnUm0"