sabre_dev_studio-flight 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +54 -0
  6. data/Rakefile +20 -0
  7. data/lib/sabre_dev_studio-flight/airports_at_cities_lookup.rb +13 -0
  8. data/lib/sabre_dev_studio-flight/api.rb +209 -0
  9. data/lib/sabre_dev_studio-flight/city_pairs_lookup.rb +13 -0
  10. data/lib/sabre_dev_studio-flight/destination_finder.rb +13 -0
  11. data/lib/sabre_dev_studio-flight/fare_range.rb +13 -0
  12. data/lib/sabre_dev_studio-flight/instaflights_search.rb +13 -0
  13. data/lib/sabre_dev_studio-flight/lead_price_calendar.rb +13 -0
  14. data/lib/sabre_dev_studio-flight/low_fare_forecast.rb +13 -0
  15. data/lib/sabre_dev_studio-flight/multiairport_city_lookup.rb +13 -0
  16. data/lib/sabre_dev_studio-flight/shop/streamlined_destinations.rb +38 -0
  17. data/lib/sabre_dev_studio-flight/shop/streamlined_itineraries.rb +225 -0
  18. data/lib/sabre_dev_studio-flight/theme_airport_lookup.rb +13 -0
  19. data/lib/sabre_dev_studio-flight/travel_seasonality.rb +13 -0
  20. data/lib/sabre_dev_studio-flight/travel_theme_lookup.rb +13 -0
  21. data/lib/sabre_dev_studio-flight/version.rb +5 -0
  22. data/lib/sabre_dev_studio-flight.rb +6 -0
  23. data/sabre_dev_studio-flight.gemspec +24 -0
  24. data/test/api_test.rb +194 -0
  25. data/test/fixtures/air_shopping_themes.json +113 -0
  26. data/test/fixtures/airports_at_cities_lookup.json +26 -0
  27. data/test/fixtures/city_pairs.json +16708 -0
  28. data/test/fixtures/destination_air_shop.json +481 -0
  29. data/test/fixtures/fare_range.json +35 -0
  30. data/test/fixtures/future_dates_lead_fare_shop.json +2525 -0
  31. data/test/fixtures/low_fare_forecast.json +28 -0
  32. data/test/fixtures/multiairport_city_lookup.json +164 -0
  33. data/test/fixtures/single_date_air_shop.json +352 -0
  34. data/test/fixtures/theme_airport_lookup.json +77 -0
  35. data/test/fixtures/theme_airports.json +32 -0
  36. data/test/fixtures/travel_seasonality.json +379 -0
  37. data/test/test_helper.rb +32 -0
  38. metadata +149 -0
@@ -0,0 +1,225 @@
1
+ =begin
2
+
3
+ options = {
4
+ :origin => 'ATL',
5
+ :destination => 'LAS',
6
+ :departuredate => '2014-06-01',
7
+ :returndate => '2014-06-05',
8
+ :limit => 300
9
+ }
10
+ itineraries = SabreDevStudio::Flight::Api.single_date_air_shop(options); nil
11
+ streamlined_itineraries = SabreDevStudio::Flight::Shop::StreamlinedItineraries.new(itineraries.response['PricedItineraries']); nil
12
+
13
+ -- or --
14
+
15
+ require 'json'
16
+ itineraries = JSON.parse(File.read('./test/fixtures/single_date_air_shop.json')); nil
17
+ streamlined_itineraries = SabreDevStudio::Flight::Shop::StreamlinedItineraries.new(itineraries['PricedItineraries']); nil
18
+ pp streamlined_itineraries; nil
19
+
20
+ =end
21
+
22
+ # require 'digest'
23
+ module SabreDevStudio
24
+ module Flight
25
+ module Shop
26
+ class StreamlinedItineraries
27
+ attr_reader :flights, :outbounds, :min_price, :max_price,
28
+ :min_outbound_duration, :max_outbound_duration, :airline_codes
29
+
30
+ def initialize(itineraries)
31
+ @flights = []
32
+ @outbounds = []
33
+ @min_price = @max_price = nil
34
+ @min_outbound_duration = @max_outbound_duration = nil
35
+ outbound_flights = Hash.new
36
+ outbound_flight = nil
37
+
38
+ itineraries.each do |itin|
39
+ segments = itin['AirItinerary']['OriginDestinationOptions']['OriginDestinationOption']
40
+ amount = itin['AirItineraryPricingInfo']['ItinTotalFare']['TotalFare']['Amount'].to_f
41
+ segments.each_with_index.each do |segment, idx|
42
+ direction = idx % 2 == 0 ? 'outbound' : 'inbound'
43
+ flight = Flight.new(segment['FlightSegment'], direction, segment['ElapsedTime'])
44
+ @flights << flight.to_hash
45
+
46
+ set_price(amount)
47
+ if flight.direction == 'outbound'
48
+ set_duration(flight.duration)
49
+ outbound_flight = flight
50
+ outbound_flights[outbound_flight.sha] ||= OutboundFlight.new(flight, amount)
51
+ else
52
+ outbound_flights[outbound_flight.sha].add_inbound_flight(flight, amount)
53
+ end
54
+
55
+ if segment['FlightSegment'].is_a?(Array)
56
+ segment['FlightSegment'].each_with_index do |flight_segment, idx|
57
+ # next if idx > 0
58
+ connection = Flight.new(flight_segment, 'connecting', segment['ElapsedTime'])
59
+ next if connection.sha == flight.sha
60
+ @flights << connection.to_hash
61
+ flight.add_connecting_flight(connection)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ @airline_codes = @flights.map { |f| f[:airlines] }.flatten.uniq.sort
68
+ @flights.uniq!
69
+ @outbounds = outbound_flights.values.map(&:to_hash)
70
+ end
71
+
72
+ def set_price(fare)
73
+ @min_price = fare if @min_price.nil? || fare < @min_price
74
+ @max_price = fare if @max_price.nil? || fare > @max_price
75
+ end
76
+ def set_duration(duration)
77
+ @min_outbound_duration = duration if @min_outbound_duration.nil? || duration < @min_outbound_duration
78
+ @max_outbound_duration = duration if @max_outbound_duration.nil? || duration > @max_outbound_duration
79
+ end
80
+ end
81
+
82
+ class Flight
83
+ attr_reader :direction, :airlines, :duration, :flight_number,
84
+ :departure_airport, :arrival_airport,
85
+ :stops, :departure_time, :arrival_time, :connections
86
+
87
+ def initialize(flight_segment, direction, duration)
88
+ @airlines = []
89
+ @stops = []
90
+ @connections = []
91
+ @direction = direction
92
+ if direction == 'connecting'
93
+ @duration = flight_segment['ElapsedTime']
94
+ else
95
+ @duration = duration
96
+ end
97
+ parse_data(flight_segment)
98
+ end
99
+
100
+ def sha
101
+ str = "#{@airlines.first}_#{@flight_number}_#{@departure_airport}_#{@departure_time}"
102
+ # Digest::SHA1.hexdigest(str)
103
+ str
104
+ end
105
+
106
+ def to_hash
107
+ {
108
+ uid: sha,
109
+ direction: @direction,
110
+ airlines: airlines.uniq,
111
+ duration: minutes_to_time(duration),
112
+ departureAirport: departure_airport,
113
+ arrivalAirport: arrival_airport,
114
+ stops: stops,
115
+ connections: connections,
116
+ departureTime: reformat_date_time(departure_time),
117
+ arrivalTime: reformat_date_time(arrival_time)
118
+ }
119
+ end
120
+
121
+ def add_connecting_flight(flight)
122
+ @connections << flight.sha
123
+ end
124
+
125
+ private
126
+
127
+ def parse_data(flight_segment)
128
+ if flight_segment.is_a?(Array)
129
+ flight_segment.each_with_index do |segment, idx|
130
+ if idx == 0
131
+ @departure_airport = segment['DepartureAirport']['LocationCode']
132
+ @departure_time = segment['DepartureDateTime']
133
+ @stops << segment['ArrivalAirport']['LocationCode']
134
+ @airlines << segment['MarketingAirline']['Code']
135
+ @flight_number = segment['FlightNumber']
136
+ elsif idx == flight_segment.length - 1
137
+ @arrival_airport = segment['ArrivalAirport']['LocationCode']
138
+ @arrival_time = segment['ArrivalDateTime']
139
+ @airlines << segment['MarketingAirline']['Code']
140
+ else
141
+ @stops << segment['ArrivalAirport']['LocationCode']
142
+ @airlines << segment['MarketingAirline']['Code']
143
+ end
144
+ end
145
+ else
146
+ @departure_airport = flight_segment['DepartureAirport']['LocationCode']
147
+ @arrival_airport = flight_segment['ArrivalAirport']['LocationCode']
148
+ @departure_time = flight_segment['DepartureDateTime']
149
+ @arrival_time = flight_segment['ArrivalDateTime']
150
+ @airlines << flight_segment['MarketingAirline']['Code']
151
+ @flight_number = flight_segment['FlightNumber']
152
+ end
153
+ end
154
+
155
+ def minutes_to_time(minutes)
156
+ minutes = minutes.to_i
157
+ {
158
+ hour: minutes / 60,
159
+ minutes: minutes % 60,
160
+ raw: minutes
161
+ }
162
+ end
163
+
164
+ # "2013-06-01T09:56:00" --> "9:56 am"
165
+ # "2013-06-01T19:06:00" --> "7:06 pm"
166
+ def reformat_date_time(datetime)
167
+ datetime =~ /\d+-\d+-\d+T(\d+):(\d+):\d+/
168
+ hour, minute = $1.to_i, $2.to_i
169
+ am_pm = hour < 12 ? 'am' : 'pm'
170
+
171
+ hour = hour % 12
172
+ hour = hour == 0 ? 12 : hour
173
+ minute = sprintf("%02d", minute)
174
+ "#{hour}:#{minute} #{am_pm}"
175
+ end
176
+ end
177
+
178
+ class OutboundFlight
179
+ attr_reader :flight_uuid, :inbounds, :min_price, :max_price,
180
+ :duration, :min_inbound_duration, :max_inbound_duration
181
+
182
+ def initialize(flight, price)
183
+ @flight_uuid = flight.sha
184
+ @min_price = @max_price = price
185
+ @inbounds = []
186
+ @duration = flight.duration
187
+ @min_inbound_duration = @max_inbound_duration = nil
188
+ end
189
+
190
+ def add_inbound_flight(flight, price)
191
+ set_min_price(price)
192
+ set_inbound_duration(flight.duration)
193
+ @inbounds << {
194
+ flight_uuid: flight.sha,
195
+ price: price
196
+ }
197
+ end
198
+
199
+ def to_hash
200
+ {
201
+ flight_uuid: flight_uuid,
202
+ min_price: min_price,
203
+ max_price: max_price,
204
+ duration: duration,
205
+ min_inbound_duration: min_inbound_duration,
206
+ max_inbound_duration: max_inbound_duration,
207
+ inbounds: inbounds.uniq
208
+ }
209
+ end
210
+
211
+ private
212
+
213
+ def set_min_price(price)
214
+ @min_price = price if price < @min_price
215
+ @max_price = price if price > @max_price
216
+ end
217
+
218
+ def set_inbound_duration(duration)
219
+ @min_inbound_duration = duration if @min_inbound_duration.nil? || duration < @min_inbound_duration
220
+ @max_inbound_duration = duration if @max_inbound_duration.nil? || duration > @max_inbound_duration
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,13 @@
1
+ module SabreDevStudio
2
+ module Flight
3
+
4
+ ##
5
+ # Air Shopping Themes Convenience Object
6
+ #
7
+ # ==== Documentation:
8
+ # https://developer.sabre.com/docs/read/rest_apis/theme_airport_lookup
9
+ class ThemeAirportLookup < SabreDevStudio::RequestObject
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module SabreDevStudio
2
+ module Flight
3
+
4
+ ##
5
+ # Travel Seasonality
6
+ #
7
+ # ==== Documentation:
8
+ # https://developer.sabre.com/docs/read/rest_apis/travel_seasonality
9
+ class TravelSeasonality < SabreDevStudio::RequestObject
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module SabreDevStudio
2
+ module Flight
3
+
4
+ ##
5
+ # Air Shopping Themes Convenience Object
6
+ #
7
+ # ==== Documentation:
8
+ # https://developer.sabre.com/docs/read/rest_apis/travel_theme_lookup
9
+ class TravelThemeLookup < SabreDevStudio::RequestObject
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module SabreDevStudio
2
+ module Flight
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ require 'sabre_dev_studio'
2
+ require File.expand_path("../sabre_dev_studio-flight/api", __FILE__)
3
+ require File.expand_path("../sabre_dev_studio-flight/version", __FILE__)
4
+
5
+ require File.expand_path("../sabre_dev_studio-flight/shop/streamlined_destinations", __FILE__)
6
+ require File.expand_path("../sabre_dev_studio-flight/shop/streamlined_itineraries", __FILE__)
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sabre_dev_studio-flight/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "sabre_dev_studio-flight"
8
+ gem.version = SabreDevStudio::Flight::VERSION
9
+ gem.authors = ["Barrett Clark"]
10
+ gem.email = ["barrett.clark@sabre.com"]
11
+ gem.description = %q{Access the Sabre Travel Platform Services (TPS) Dev Studio Flight API}
12
+ gem.summary = %q{Access the Sabre Dev Studio API}
13
+ gem.homepage = "http://developer.sabre.com"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'geminabox'
21
+ gem.add_development_dependency 'webmock'
22
+ gem.add_development_dependency 'rdoc'
23
+ gem.add_runtime_dependency 'sabre_dev_studio'
24
+ end
data/test/api_test.rb ADDED
@@ -0,0 +1,194 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class ApiTests < Test::Unit::TestCase
4
+ def setup
5
+ SabreDevStudio.configure do |c|
6
+ c.user = 'USER'
7
+ c.group = 'GROUP'
8
+ c.domain = 'DOMAIN'
9
+ c.password = 'PASSWORD'
10
+ c.uri = 'https://api.test.sabre.com'
11
+ end
12
+ stub_request(:post, "https://VjE6VVNFUjpHUk9VUDpET01BSU4%3D:UEFTU1dPUkQ%3D@api.test.sabre.com/v1/auth/token").
13
+ to_return(:status => 200, :body =>"{\"access_token\":\"Shared/IDL:IceSess\\\\/SessMgr:1\\\\.0.IDL/Common/!ICESMS\\\\/ACPCRTD!ICESMSLB\\\\/CRT.LB!-3801964284027024638!507667!0!F557CBE649675!E2E-1\",\"token_type\":\"bearer\",\"expires_in\":1800}")
14
+ end
15
+
16
+ def test_air_shopping_theme_api
17
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/shop/themes").
18
+ to_return(json_response('air_shopping_themes.json'))
19
+ air_shopping_themes = SabreDevStudio::Flight::Api.air_shopping_themes
20
+ assert_equal 11, air_shopping_themes.response['Themes'].count
21
+ assert_equal 'BEACH', air_shopping_themes.response['Themes'].first['Theme']
22
+ assert_equal 'BEACH', air_shopping_themes.themes.first.theme
23
+ end
24
+
25
+ def test_theme_airport_lookup_api
26
+ theme = 'DISNEY'
27
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/shop/themes/#{theme}").
28
+ to_return(json_response('theme_airport_lookup.json'))
29
+ airports = SabreDevStudio::Flight::Api.theme_airport_lookup(theme)
30
+ assert_equal 7, airports.destinations.count
31
+ assert_equal 'BUR', airports.destinations.first.destination
32
+ end
33
+
34
+ def test_destination_air_shop_api
35
+ options = {
36
+ :origin => 'LAS',
37
+ :lengthofstay => 1,
38
+ :theme => 'MOUNTAINS'
39
+ }
40
+ uri = Addressable::URI.new
41
+ uri.query_values = options
42
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/shop/flights/fares?#{uri.query}").
43
+ to_return(json_response('destination_air_shop.json'))
44
+ fares = SabreDevStudio::Flight::Api.destination_air_shop(options)
45
+ assert_equal options[:origin], fares.origin_location
46
+ assert_equal 31, fares.fare_info.count
47
+ assert_equal 158, fares.fare_info.first.lowest_fare
48
+ date = SabreDevStudio::Helpers::DateExtensions.make_date(fares.fare_info.first.departure_date_time)
49
+ assert_equal 4, date.month
50
+ assert_equal options[:origin], fares.response['OriginLocation']
51
+ assert_equal 158, fares.response['FareInfo'].first['LowestFare']
52
+ end
53
+
54
+ def test_future_dates_lead_fare_shop_api
55
+ options = {
56
+ :origin => 'JFK',
57
+ :destination => 'LAX',
58
+ :lengthofstay => 5
59
+ }
60
+ uri = Addressable::URI.new
61
+ uri.query_values = options
62
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/shop/flights/fares?#{uri.query}").
63
+ to_return(json_response('future_dates_lead_fare_shop.json'))
64
+ fares = SabreDevStudio::Flight::Api.future_dates_lead_fare_shop(options)
65
+ assert_equal options[:origin], fares.origin_location
66
+ assert_equal 193, fares.fare_info.count
67
+ assert_equal 792, fares.fare_info.first.lowest_fare
68
+ assert_equal options[:origin], fares.response['OriginLocation']
69
+ assert_equal 792, fares.response['FareInfo'].first['LowestFare']
70
+ end
71
+
72
+ def test_single_date_air_shop_api
73
+ options = {
74
+ :origin => 'JFK',
75
+ :destination => 'LAX',
76
+ :departuredate => '2014-10-01',
77
+ :returndate => '2014-10-05',
78
+ :onlineitinerariesonly => 'N',
79
+ :limit => 1,
80
+ :offset => 1,
81
+ :sortby => 'totalfare',
82
+ :order => 'asc',
83
+ :sortby2 => 'departuretime',
84
+ :order2 => 'dsc'
85
+ }
86
+ uri = Addressable::URI.new
87
+ uri.query_values = options
88
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/shop/flights?#{uri.query}").
89
+ to_return(json_response('single_date_air_shop.json'))
90
+ fares = SabreDevStudio::Flight::Api.single_date_air_shop(options)
91
+ assert_equal options[:origin], fares.origin_location
92
+ assert_equal 387.0, fares.priced_itineraries.first.air_itinerary_pricing_info.itin_total_fare.total_fare.amount
93
+ assert_equal options[:origin], fares.response['OriginLocation']
94
+ assert_equal 387, fares.response['PricedItineraries'].first['AirItineraryPricingInfo']['ItinTotalFare']['TotalFare']['Amount']
95
+ end
96
+
97
+ def test_low_fare_forecast_api
98
+ options = {
99
+ :origin => 'JFK',
100
+ :destination => 'LAX',
101
+ :departuredate => '2014-10-01',
102
+ :returndate => '2014-10-05'
103
+ }
104
+ uri = Addressable::URI.new
105
+ uri.query_values = options
106
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/forecast/flights/fares?#{uri.query}").
107
+ to_return(json_response('low_fare_forecast.json'))
108
+ forecast = SabreDevStudio::Flight::Api.low_fare_forecast(options)
109
+ assert_equal options[:origin], forecast.origin_location
110
+ assert_equal options[:origin], forecast.response['OriginLocation']
111
+ assert_equal options[:destination], forecast.response['DestinationLocation']
112
+ assert_equal 387.0, forecast.response['LowestFare']
113
+ end
114
+
115
+ def test_fare_range_api
116
+ options = {
117
+ :origin => 'JFK',
118
+ :destination => 'LAX',
119
+ :earliestdeparturedate => '2014-06-01',
120
+ :latestdeparturedate => '2014-06-01',
121
+ :lengthofstay => 4
122
+ }
123
+ uri = Addressable::URI.new
124
+ uri.query_values = options
125
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/historical/flights/fares?#{uri.query}").
126
+ to_return(json_response('fare_range.json'))
127
+ fare_range = SabreDevStudio::Flight::Api.fare_range(options)
128
+ assert_equal options[:origin], fare_range.origin_location
129
+ assert_equal options[:destination], fare_range.destination_location
130
+ assert_equal 1, fare_range.fare_data.count
131
+ assert_equal options[:origin], fare_range.response['OriginLocation']
132
+ assert_equal options[:destination], fare_range.response['DestinationLocation']
133
+ assert_equal 1, fare_range.response['FareData'].count
134
+ end
135
+
136
+ def test_travel_seasonality_api
137
+ destination = 'DFW'
138
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/historical/flights/#{destination}/seasonality").
139
+ to_return(json_response('travel_seasonality.json'))
140
+ travel_seasonality = SabreDevStudio::Flight::Api.travel_seasonality(destination)
141
+ assert_equal destination, travel_seasonality.destination_location
142
+ assert_equal 52, travel_seasonality.seasonality.count
143
+ assert_equal 1, travel_seasonality.seasonality.first.year_week_number
144
+ assert_equal destination, travel_seasonality.response['DestinationLocation']
145
+ assert_equal 52, travel_seasonality.response['Seasonality'].count
146
+ assert_equal 1, travel_seasonality.response['Seasonality'].first['YearWeekNumber']
147
+ assert_equal 'Low', travel_seasonality.response['Seasonality'].first['SeasonalityIndicator']
148
+ end
149
+
150
+ def test_city_pairs_lookup_api
151
+ options = {
152
+ :origincountry => 'US',
153
+ :destinationcountry => 'US'
154
+ }
155
+ uri = Addressable::URI.new
156
+ uri.query_values = options
157
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/lists/airports/supported/origins-destinations?#{uri.query}").
158
+ to_return(json_response('city_pairs.json'))
159
+ city_pairs = SabreDevStudio::Flight::Api.city_pairs_lookup(options)
160
+ assert_equal 982, city_pairs.origin_destination_locations.count
161
+ assert_equal 'DEN', city_pairs.origin_destination_locations.first.origin_location.airport_code
162
+ assert_equal 'ABQ', city_pairs.origin_destination_locations.first.destination_location.airport_code
163
+ assert_equal 982, city_pairs.response['OriginDestinationLocations'].count
164
+ assert_equal 'DEN', city_pairs.response['OriginDestinationLocations'].first['OriginLocation']['AirportCode']
165
+ assert_equal 'ABQ', city_pairs.response['OriginDestinationLocations'].first['DestinationLocation']['AirportCode']
166
+ end
167
+
168
+ def test_multiairport_city_lookup_api
169
+ options = { :country => 'US' }
170
+ uri = Addressable::URI.new
171
+ uri.query_values = options
172
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/lists/cities?#{uri.query}").
173
+ to_return(json_response('multiairport_city_lookup.json'))
174
+ city_lookup = SabreDevStudio::Flight::Api.multiairport_city_lookup(options)
175
+ assert_equal 15, city_lookup.cities.count
176
+ assert_equal 'WAS', city_lookup.cities.last.code
177
+ assert_equal 15, city_lookup.response['Cities'].count
178
+ assert_equal 'WAS', city_lookup.response['Cities'].last['code']
179
+ end
180
+
181
+ def test_airports_at_cities_lookup_api
182
+ options = { :city => 'QDF' }
183
+ uri = Addressable::URI.new
184
+ uri.query_values = options
185
+ stub_request(:get, "#{SabreDevStudio.configuration.uri}/v1/lists/airports?#{uri.query}").
186
+ to_return(json_response('airports_at_cities_lookup.json'))
187
+ airports = SabreDevStudio::Flight::Api.airports_at_cities_lookup(options)
188
+ assert_equal 3, airports.airports.count
189
+ assert_equal 'EWR', airports.airports.first.code
190
+ assert_equal 'NEWARK', airports.airports.first.name
191
+ assert_equal 3, airports.response['Airports'].count
192
+ assert_equal 'EWR', airports.response['Airports'].first['code']
193
+ end
194
+ end
@@ -0,0 +1,113 @@
1
+ {
2
+ "Themes": [
3
+ {
4
+ "Theme": "BEACH",
5
+ "Links": [
6
+ {
7
+ "rel": "destinations",
8
+ "href": "https://api.test.sabre.com/v1/shop/themes/BEACH"
9
+ }
10
+ ]
11
+ },
12
+ {
13
+ "Theme": "DISNEY",
14
+ "Links": [
15
+ {
16
+ "rel": "destinations",
17
+ "href": "https://api.test.sabre.com/v1/shop/themes/DISNEY"
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "Theme": "GAMBLING",
23
+ "Links": [
24
+ {
25
+ "rel": "destinations",
26
+ "href": "https://api.test.sabre.com/v1/shop/themes/GAMBLING"
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "Theme": "HISTORIC",
32
+ "Links": [
33
+ {
34
+ "rel": "destinations",
35
+ "href": "https://api.test.sabre.com/v1/shop/themes/HISTORIC"
36
+ }
37
+ ]
38
+ },
39
+ {
40
+ "Theme": "MOUNTAINS",
41
+ "Links": [
42
+ {
43
+ "rel": "destinations",
44
+ "href": "https://api.test.sabre.com/v1/shop/themes/MOUNTAINS"
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ "Theme": "NATIONAL-PARKS",
50
+ "Links": [
51
+ {
52
+ "rel": "destinations",
53
+ "href": "https://api.test.sabre.com/v1/shop/themes/NATIONAL-PARKS"
54
+ }
55
+ ]
56
+ },
57
+ {
58
+ "Theme": "OUTDOORS",
59
+ "Links": [
60
+ {
61
+ "rel": "destinations",
62
+ "href": "https://api.test.sabre.com/v1/shop/themes/OUTDOORS"
63
+ }
64
+ ]
65
+ },
66
+ {
67
+ "Theme": "ROMANTIC",
68
+ "Links": [
69
+ {
70
+ "rel": "destinations",
71
+ "href": "https://api.test.sabre.com/v1/shop/themes/ROMANTIC"
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ "Theme": "SHOPPING",
77
+ "Links": [
78
+ {
79
+ "rel": "destinations",
80
+ "href": "https://api.test.sabre.com/v1/shop/themes/SHOPPING"
81
+ }
82
+ ]
83
+ },
84
+ {
85
+ "Theme": "SKIING",
86
+ "Links": [
87
+ {
88
+ "rel": "destinations",
89
+ "href": "https://api.test.sabre.com/v1/shop/themes/SKIING"
90
+ }
91
+ ]
92
+ },
93
+ {
94
+ "Theme": "THEME-PARK",
95
+ "Links": [
96
+ {
97
+ "rel": "destinations",
98
+ "href": "https://api.test.sabre.com/v1/shop/themes/THEME-PARK"
99
+ }
100
+ ]
101
+ }
102
+ ],
103
+ "Links": [
104
+ {
105
+ "rel": "self",
106
+ "href": "https://api.test.sabre.com/v1/shop/themes"
107
+ },
108
+ {
109
+ "rel": "linkTemplate",
110
+ "href": "https://api.test.sabre.com/v1/shop/themes"
111
+ }
112
+ ]
113
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "Airports": [
3
+ {
4
+ "code": "EWR",
5
+ "name": "NEWARK"
6
+ },
7
+ {
8
+ "code": "JFK",
9
+ "name": "NEW YORK JFK"
10
+ },
11
+ {
12
+ "code": "LGA",
13
+ "name": "NEW YORK LGA"
14
+ }
15
+ ],
16
+ "Links": [
17
+ {
18
+ "rel": "self",
19
+ "href": "https://api.test.sabre.com/v1/lists/airports?country=US&city=NYC"
20
+ },
21
+ {
22
+ "rel": "linkTemplate",
23
+ "href": "https://api.test.sabre.com/v1/lists/airports?city=<city>"
24
+ }
25
+ ]
26
+ }