diff --git a/README.md b/README.md index 7e625bfe..e5027b27 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,14 @@ Hotel[] hotels = amadeus.referenceData.locations.hotels.byGeocode.get(Params .with("longitude", 2.160873) .and("latitude", 41.397158)); +// Hotel autocomplete names +Hotel[] result = amadeus.referenceData.locations.hotel.get(Params + .with("keyword", "PARI") + .and("subType", "HOTEL_GDS") + .and("countryCode", "FR") + .and("lang", "EN") + .and("max", "20")); + // Hotel Offers Search API v3 // Get multiple hotel offers HotelOfferSearch[] offers = amadeus.shopping.hotelOffersSearch.get(Params diff --git a/src/main/java/com/amadeus/referenceData/Locations.java b/src/main/java/com/amadeus/referenceData/Locations.java index dfd2d102..93f97fb1 100644 --- a/src/main/java/com/amadeus/referenceData/Locations.java +++ b/src/main/java/com/amadeus/referenceData/Locations.java @@ -6,6 +6,7 @@ import com.amadeus.exceptions.ResponseException; import com.amadeus.referenceData.locations.Airports; import com.amadeus.referenceData.locations.Cities; +import com.amadeus.referenceData.locations.Hotel; import com.amadeus.referenceData.locations.Hotels; import com.amadeus.referenceData.locations.PointOfInterest; import com.amadeus.referenceData.locations.PointsOfInterest; @@ -67,6 +68,13 @@ public class Locations { */ public Hotels hotels; + /** + *

+ * A namespaced client for the + * /v1/reference-data/locations/hotel endpoints. + *

+ */ + public Hotel hotel; /** *

* A namespaced client for the @@ -84,6 +92,7 @@ public Locations(Amadeus client) { this.airports = new Airports(client); this.pointsOfInterest = new PointsOfInterest(client); this.hotels = new Hotels(client); + this.hotel = new Hotel(client); this.cities = new Cities(client); } diff --git a/src/main/java/com/amadeus/referenceData/locations/Hotel.java b/src/main/java/com/amadeus/referenceData/locations/Hotel.java new file mode 100644 index 00000000..44a6af59 --- /dev/null +++ b/src/main/java/com/amadeus/referenceData/locations/Hotel.java @@ -0,0 +1,52 @@ +package com.amadeus.referenceData.locations; + +import com.amadeus.Amadeus; +import com.amadeus.Params; +import com.amadeus.Response; +import com.amadeus.exceptions.ResponseException; +import com.amadeus.resources.Resource; + +/** + *

+ * A namespaced client for the + * /v1/reference-data/locations/hotel endpoints. + *

+ * + *

+ * Access via the Amadeus client object. + *

+ * + *
+ * Amadeus amadeus = Amadeus.builder("clientId", "secret").build();
+ * amadeus.referenceData.locations.hotel;
+ */ +public class Hotel { + private Amadeus client; + + /** + * Constructor. + * @hide + */ + public Hotel(Amadeus client) { + this.client = client; + } + + /** + *

+ * Returns a list of relevant hotels inside a city. + *

+ * + *
+   * amadeus.referenceData.locations.hotel.get(Params
+   *   .with("cityCode", "PAR"));
+ * + * @param params the parameters to send to the API + * @return an API response object + * @throws ResponseException when an exception occurs + */ + public com.amadeus.resources.Hotel[] get(Params params) throws ResponseException { + Response response = client.get("/v1/reference-data/locations/hotel", params); + return (com.amadeus.resources.Hotel[]) + Resource.fromArray(response, com.amadeus.resources.Hotel[].class); + } +} diff --git a/src/test/java/com/amadeus/NamespaceTest.java b/src/test/java/com/amadeus/NamespaceTest.java index c93d114b..047c713a 100644 --- a/src/test/java/com/amadeus/NamespaceTest.java +++ b/src/test/java/com/amadeus/NamespaceTest.java @@ -20,6 +20,7 @@ import com.amadeus.referenceData.RecommendedLocations; import com.amadeus.referenceData.locations.Airports; import com.amadeus.referenceData.locations.Cities; +import com.amadeus.referenceData.locations.Hotel; import com.amadeus.referenceData.locations.PointsOfInterest; import com.amadeus.referenceData.locations.hotels.ByCity; import com.amadeus.referenceData.locations.hotels.ByGeocode; @@ -56,6 +57,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; +// TODO Create an isolated Unit tests per entity. public class NamespaceTest { private Amadeus client; @@ -531,6 +533,12 @@ public void testGetMethods() throws ResponseException { assertNotNull(hotelsByGeocode.get(params)); assertEquals(hotelsByGeocode.get().length, 2); + // Testing hotel autocomplete feature + Hotel hotel = new Hotel(client); + Mockito.when(client.get("/v1/reference-data/locations/hotel", params)) + .thenReturn(multiResponse); + assertNotNull(hotel.get(params)); + // Testing city search get Mockito.when(client.get("/v1/reference-data/locations/cities", null)) .thenReturn(multiResponse); diff --git a/src/test/java/com/amadeus/referenceData/locations/HotelIT.java b/src/test/java/com/amadeus/referenceData/locations/HotelIT.java new file mode 100644 index 00000000..85cf37c6 --- /dev/null +++ b/src/test/java/com/amadeus/referenceData/locations/HotelIT.java @@ -0,0 +1,156 @@ +package com.amadeus.referenceData.locations; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.assertj.core.api.BDDAssertions.then; + +import com.amadeus.Amadeus; +import com.amadeus.Params; +import com.amadeus.exceptions.ResponseException; +import com.amadeus.resources.Hotel; + +import com.github.tomakehurst.wiremock.WireMockServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class HotelIT { + + WireMockServer wireMockServer; + + private Amadeus amadeus; + + /** + * In every tests, we will authenticate. + */ + @BeforeEach + public void setup() { + wireMockServer = new WireMockServer(8080); + wireMockServer.start(); + + //https://developers.amadeus.com/self-service/apis-docs/guides/authorization-262 + String address = "/v1/security/oauth2/token" + + "?grant_type=client_credentials&client_secret=DEMO&client_id=DEMO"; + wireMockServer.stubFor(post(urlEqualTo(address)) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withStatus(200) + .withBodyFile("auth_ok.json"))); + + amadeus = Amadeus + .builder("DEMO", "DEMO") + .setHost("localhost") + .setPort(8080) + .setSsl(false) + .setLogLevel("debug") + .build(); + } + + @AfterEach + public void teardown() { + wireMockServer.stop(); + } + + @Test + public void given_client_when_call_hotel_with_mandatory_parameters_then_returns_ok() + throws ResponseException { + + //Given + Params params = Params + .with("keyword", "PARI") + .and("subType", "HOTEL_GDS"); + + String urlParams = "?subType=HOTEL_GDS&keyword=PARI"; + String address = "/v1/reference-data/locations/hotel" + urlParams; + wireMockServer.stubFor(get(urlEqualTo(address)) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withStatus(200) + .withBodyFile("reference_data_hotel_default_response_ok.json"))); + + //When + Hotel[] result = amadeus.referenceData.locations.hotel.get(params); + + //Then + then(result).isNotNull(); + then(result.length).isGreaterThan(1); + } + + @Test + public void given_client_when_call_hotel_then_returns_single_hotel_response_ok() + throws ResponseException { + + //Given + Params params = Params + .with("keyword", "PARI") + .and("subType", "HOTEL_GDS") + .and("max", "1"); + + String urlParams = "?max=1&subType=HOTEL_GDS&keyword=PARI"; + String address = "/v1/reference-data/locations/hotel" + urlParams; + wireMockServer.stubFor(get(urlEqualTo(address)) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withStatus(200) + .withBodyFile("reference_data_hotel_single_hotel_response_ok.json"))); + + //When + Hotel[] result = amadeus.referenceData.locations.hotel.get(params); + + //Then + then(result).isNotNull(); + then(result.length).isEqualTo(1); + } + + @Test + public void given_client_when_call_hotel_then_returns_multiple_hotel_response_ok() + throws ResponseException { + + //Given + Params params = Params + .with("keyword", "PARI") + .and("subType", "HOTEL_GDS") + .and("max", "5"); + + String urlParams = "?max=5&subType=HOTEL_GDS&keyword=PARI"; + String address = "/v1/reference-data/locations/hotel" + urlParams; + wireMockServer.stubFor(get(urlEqualTo(address)) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withStatus(200) + .withBodyFile("reference_data_hotel_multiple_hotel_response_ok.json"))); + + //When + Hotel[] result = amadeus.referenceData.locations.hotel.get(params); + + //Then + then(result).isNotNull(); + then(result.length).isEqualTo(5); + } + + @Test + public void given_client_when_call_hotel_with_all_parameters_then_response_ok() + throws ResponseException { + + //Given + Params params = Params + .with("keyword", "PARI") + .and("subType", "HOTEL_GDS") + .and("countryCode", "FR") + .and("lang", "EN") + .and("max", "20"); + + String urlParams = "?max=20&countryCode=FR&subType=HOTEL_GDS&keyword=PARI&lang=EN"; + String address = "/v1/reference-data/locations/hotel" + urlParams; + wireMockServer.stubFor(get(urlEqualTo(address)) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withStatus(200) + .withBodyFile("reference_data_hotel_default_response_ok.json"))); + + //When + Hotel[] result = amadeus.referenceData.locations.hotel.get(params); + + //Then + then(result).isNotNull(); + then(result.length).isGreaterThan(1); + } +} diff --git a/src/test/resources/__files/reference_data_hotel_default_response_ok.json b/src/test/resources/__files/reference_data_hotel_default_response_ok.json new file mode 100644 index 00000000..47f79b8f --- /dev/null +++ b/src/test/resources/__files/reference_data_hotel_default_response_ok.json @@ -0,0 +1,386 @@ +{ + "data": [ + { + "id": 3445495, + "name": "PARIS PARA", + "iataCode": "ABL", + "subType": "HOTEL_GDS", + "relevance": 70, + "type": "location", + "hotelIds": [ + "OIABLIAD" + ], + "address": { + "cityName": "AMBLER", + "countryCode": "US", + "stateCode": "AK" + }, + "geoCode": { + "latitude": 67.0862, + "longitude": -157.85701 + } + }, + { + "id": 3422865, + "name": "CITADINES BASTILLE MARAIS PARI", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 70, + "type": "location", + "hotelIds": [ + "AZPAROBA" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.8581, + "longitude": 2.37113 + } + }, + { + "id": 3408849, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "MIA", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "EAMIAMAP" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 25.77775, + "longitude": -80.30379 + } + }, + { + "id": 3408846, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "MIA", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "EAMIA276" + ], + "address": { + "cityName": "MIAMI", + "countryCode": "US", + "stateCode": "FL" + }, + "geoCode": { + "latitude": 25.79686, + "longitude": -80.31452 + } + }, + { + "id": 3408848, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "MIA", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "EAMIACCT" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.85721, + "longitude": 2.34144 + } + }, + { + "id": 1643792, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "LON", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "HILONF17" + ], + "address": { + "cityName": "HARLOW", + "countryCode": "GB" + }, + "geoCode": { + "latitude": 51.76906, + "longitude": 0.09654 + } + }, + { + "id": 3451863, + "name": "IBIS PARIS CDG PARIS NORD 2", + "iataCode": "CDG", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "RTCDGPND" + ], + "address": { + "cityName": "ROISSY EN FRANCE", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.98904, + "longitude": 2.51303 + } + }, + { + "id": 3544748, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "JEG", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "ZZJEG119" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.8569, + "longitude": 2.35085 + } + }, + { + "id": 1247838, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "LON", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "HILON638" + ], + "address": { + "cityName": "LONDON", + "countryCode": "GB" + }, + "geoCode": { + "latitude": 51.54846, + "longitude": -0.18098 + } + }, + { + "id": 3562479, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "JEG", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "GTJEG119" + ], + "address": { + "cityName": "AASIAAT", + "countryCode": "GL" + }, + "geoCode": { + "latitude": 69.29362, + "longitude": 80.27399 + } + }, + { + "id": 3234104, + "name": "KYRIAD PARIS 10 GARE DU NORD", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "NNPAR585" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.87967, + "longitude": 2.35594 + } + }, + { + "id": 1137534, + "name": "IBIS PARIS LE BOURGET", + "iataCode": "LBG", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "RTLBGIBI" + ], + "address": { + "cityName": "LA COURNEUVE", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.93255, + "longitude": 2.41023 + } + }, + { + "id": 1525855, + "name": "RELAIS HOTEL DU VIEUX PARIS", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "ACPARC06" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.89656, + "longitude": 2.34371 + } + }, + { + "id": 1346610, + "name": "LE CHAPLAIN PARIS RIVE GAUCHE", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "ACPARCHA" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.84292, + "longitude": 2.33037 + } + }, + { + "id": 1454060, + "name": "VILLA MAZARIN PARIS", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "ACPARB25" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.87105, + "longitude": 2.32037 + } + }, + { + "id": 3061349, + "name": "LYRIC HOTEL PARIS", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "DHPARCCL" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.86859, + "longitude": 2.33316 + } + }, + { + "id": 2183927, + "name": "HIPOTEL PARIS MARNE LA VALLEE", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "HSPARBJC" + ], + "address": { + "cityName": "NOISIEL", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.84364, + "longitude": 2.61496 + } + }, + { + "id": 1289838, + "name": "BEST WESTERN HOTEL DE PARIS", + "iataCode": "LVA", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "BWLVA613" + ], + "address": { + "cityName": "LAVAL", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.06113, + "longitude": -0.76575 + } + }, + { + "id": 2039491, + "name": "HOTEL DE PARIS", + "iataCode": "DIJ", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "HSDIJAAG" + ], + "address": { + "cityName": "DIJON", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 47.32337, + "longitude": 5.03019 + } + }, + { + "id": 2923847, + "name": "INTER HOTEL PARISIANA", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 37, + "type": "location", + "hotelIds": [ + "YXPARHPR" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.87668, + "longitude": 2.35439 + } + } + ] +} diff --git a/src/test/resources/__files/reference_data_hotel_multiple_hotel_response_ok.json b/src/test/resources/__files/reference_data_hotel_multiple_hotel_response_ok.json new file mode 100644 index 00000000..6058b923 --- /dev/null +++ b/src/test/resources/__files/reference_data_hotel_multiple_hotel_response_ok.json @@ -0,0 +1,99 @@ +{ + "data": [ + { + "id": 3422865, + "name": "CITADINES BASTILLE MARAIS PARI", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 70, + "type": "location", + "hotelIds": [ + "AZPAROBA" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.8581, + "longitude": 2.37113 + } + }, + { + "id": 3408848, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "MIA", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "EAMIACCT" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.85721, + "longitude": 2.34144 + } + }, + { + "id": 3544748, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "JEG", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "ZZJEG119" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.8569, + "longitude": 2.35085 + } + }, + { + "id": 3451863, + "name": "IBIS PARIS CDG PARIS NORD 2", + "iataCode": "CDG", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "RTCDGPND" + ], + "address": { + "cityName": "ROISSY EN FRANCE", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.98904, + "longitude": 2.51303 + } + }, + { + "id": 3408849, + "name": "CONCORDE OPERA PARIS OPERA PARIS", + "iataCode": "MIA", + "subType": "HOTEL_GDS", + "relevance": 40, + "type": "location", + "hotelIds": [ + "EAMIAMAP" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 25.77775, + "longitude": -80.30379 + } + } + ] +} diff --git a/src/test/resources/__files/reference_data_hotel_single_hotel_response_ok.json b/src/test/resources/__files/reference_data_hotel_single_hotel_response_ok.json new file mode 100644 index 00000000..1b61c57c --- /dev/null +++ b/src/test/resources/__files/reference_data_hotel_single_hotel_response_ok.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": 3422865, + "name": "CITADINES BASTILLE MARAIS PARI", + "iataCode": "PAR", + "subType": "HOTEL_GDS", + "relevance": 70, + "type": "location", + "hotelIds": [ + "AZPAROBA" + ], + "address": { + "cityName": "PARIS", + "countryCode": "FR" + }, + "geoCode": { + "latitude": 48.8581, + "longitude": 2.37113 + } + } + ] +}