hotel_beds 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -44
  3. data/lib/hotel_beds/basket_remove/envelope.rb +16 -0
  4. data/lib/hotel_beds/basket_remove/operation.rb +36 -0
  5. data/lib/hotel_beds/basket_remove/request.rb +19 -0
  6. data/lib/hotel_beds/basket_remove/response.rb +31 -0
  7. data/lib/hotel_beds/client.rb +21 -0
  8. data/lib/hotel_beds/hotel_basket_add/request.rb +1 -1
  9. data/lib/hotel_beds/hotel_basket_add/response.rb +4 -35
  10. data/lib/hotel_beds/hotel_search/envelope.rb +10 -9
  11. data/lib/hotel_beds/hotel_search/request.rb +6 -4
  12. data/lib/hotel_beds/hotel_search/response.rb +4 -9
  13. data/lib/hotel_beds/model/available_room.rb +22 -1
  14. data/lib/hotel_beds/model/cancellation_policy.rb +0 -1
  15. data/lib/hotel_beds/model/comment.rb +13 -0
  16. data/lib/hotel_beds/model/contract.rb +15 -0
  17. data/lib/hotel_beds/model/customer.rb +21 -0
  18. data/lib/hotel_beds/model/destination.rb +13 -0
  19. data/lib/hotel_beds/model/hotel.rb +12 -7
  20. data/lib/hotel_beds/model/price.rb +18 -0
  21. data/lib/hotel_beds/model/purchase.rb +7 -2
  22. data/lib/hotel_beds/model/room.rb +0 -1
  23. data/lib/hotel_beds/model/service.rb +6 -4
  24. data/lib/hotel_beds/parser.rb +94 -0
  25. data/lib/hotel_beds/parser/available_room.rb +37 -0
  26. data/lib/hotel_beds/parser/cancellation_policy.rb +13 -0
  27. data/lib/hotel_beds/parser/comment.rb +13 -0
  28. data/lib/hotel_beds/parser/contract.rb +17 -0
  29. data/lib/hotel_beds/parser/customer.rb +18 -0
  30. data/lib/hotel_beds/parser/destination.rb +13 -0
  31. data/lib/hotel_beds/parser/errors.rb +39 -0
  32. data/lib/hotel_beds/parser/hotel.rb +27 -0
  33. data/lib/hotel_beds/parser/price.rb +14 -0
  34. data/lib/hotel_beds/parser/purchase.rb +24 -0
  35. data/lib/hotel_beds/parser/room_grouper.rb +104 -0
  36. data/lib/hotel_beds/parser/service.rb +21 -0
  37. data/lib/hotel_beds/purchase_confirm/envelope.rb +42 -0
  38. data/lib/hotel_beds/purchase_confirm/operation.rb +36 -0
  39. data/lib/hotel_beds/purchase_confirm/request.rb +72 -0
  40. data/lib/hotel_beds/purchase_confirm/response.rb +31 -0
  41. data/lib/hotel_beds/purchase_flush/envelope.rb +15 -0
  42. data/lib/hotel_beds/purchase_flush/operation.rb +36 -0
  43. data/lib/hotel_beds/purchase_flush/request.rb +17 -0
  44. data/lib/hotel_beds/purchase_flush/response.rb +26 -0
  45. data/lib/hotel_beds/version.rb +1 -1
  46. data/spec/features/flushing_the_basket_spec.rb +63 -0
  47. data/spec/features/ordering_a_hotel_room_spec.rb +96 -0
  48. data/spec/features/removing_a_basket_item_spec.rb +74 -0
  49. data/spec/lib/hotel_beds/hotel_search/request_spec.rb +4 -4
  50. data/spec/lib/hotel_beds/{hotel_search/parser → parser}/room_grouper_spec.rb +3 -3
  51. data/spec/lib/hotel_beds/parser/service_spec.rb +44 -0
  52. metadata +42 -15
  53. data/lib/hotel_beds/hotel_search/parser/errors.rb +0 -49
  54. data/lib/hotel_beds/hotel_search/parser/hotel.rb +0 -71
  55. data/lib/hotel_beds/hotel_search/parser/price.rb +0 -38
  56. data/lib/hotel_beds/hotel_search/parser/room.rb +0 -61
  57. data/lib/hotel_beds/hotel_search/parser/room_grouper.rb +0 -101
  58. data/spec/features/adding_a_hotel_to_basket_spec.rb +0 -58
  59. data/spec/features/grouped_hotel_search_spec.rb +0 -67
  60. data/spec/features/hotel_search_spec.rb +0 -113
@@ -1,71 +0,0 @@
1
- require "ostruct"
2
- require "hotel_beds/model/hotel"
3
- require "hotel_beds/hotel_search/parser/room"
4
- require "hotel_beds/hotel_search/parser/room_grouper"
5
-
6
- module HotelBeds
7
- module HotelSearch
8
- module Parser
9
- class Hotel
10
- def self.call(*args, &block)
11
- new(*args, &block).call
12
- end
13
-
14
- attr_accessor :hotel, :currency, :request
15
- private :hotel=, :currency=, :request=
16
-
17
- def initialize(hotel:, currency:, request:)
18
- self.hotel = hotel
19
- self.currency = currency
20
- self.request = request
21
- freeze
22
- end
23
-
24
- def call
25
- HotelBeds::Model::Hotel.new({
26
- availability_token: hotel.attr("availToken"),
27
- id: hotel.at_css("HotelInfo Code").content,
28
- name: hotel.at_css("HotelInfo Name").content,
29
- images: hotel.css("HotelInfo ImageList Image Url").map(&:content),
30
- latitude: hotel.at_css("HotelInfo Position").attr("latitude"),
31
- longitude: hotel.at_css("HotelInfo Position").attr("longitude"),
32
- results: results,
33
- destination_code: hotel.at_css("HotelInfo Destination").attr("code"),
34
- contract_name: hotel.at_css("ContractList Contract Name").content,
35
- contract_incoming_office_code: hotel.at_css("ContractList Contract IncomingOffice").attr("code")
36
- })
37
- end
38
-
39
- private
40
- def rooms
41
- hotel.css("AvailableRoom")
42
- end
43
-
44
- def results
45
- group_rooms(parsed_available_rooms).map do |rooms|
46
- { rooms: Array(rooms), currency: currency }
47
- end
48
- end
49
-
50
- def group_rooms(rooms)
51
- if request.group_results?
52
- grouper = HotelBeds::HotelSearch::Parser::RoomGrouper
53
- grouper.new(request.rooms, rooms).results
54
- else
55
- rooms
56
- end
57
- end
58
-
59
- def parsed_available_rooms
60
- Array(rooms).map do |room|
61
- HotelBeds::HotelSearch::Parser::Room.call(
62
- room,
63
- check_in_date: request.check_in_date,
64
- check_out_date: request.check_out_date
65
- )
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end
@@ -1,38 +0,0 @@
1
- module HotelBeds
2
- module HotelSearch
3
- module Parser
4
- class Price
5
- def self.call(*args, &block)
6
- new(*args, &block).call
7
- end
8
-
9
- attr_accessor :price
10
- private :price
11
-
12
- def initialize(price)
13
- self.price = price
14
- freeze
15
- end
16
-
17
- def call
18
- (from..to).to_a.inject(Hash.new) do |result, date|
19
- result.merge!(date => amount)
20
- end
21
- end
22
-
23
- private
24
- def from
25
- Date.parse(price.at_css("DateTimeFrom").attr("date"))
26
- end
27
-
28
- def to
29
- Date.parse(price.at_css("DateTimeTo").attr("date"))
30
- end
31
-
32
- def amount
33
- BigDecimal.new(price.at_css("Amount").content, 3)
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,61 +0,0 @@
1
- require "hotel_beds/hotel_search/parser/price"
2
- require "hotel_beds/model/available_room"
3
-
4
- module HotelBeds
5
- module HotelSearch
6
- module Parser
7
- class Room
8
- def self.call(*args, &block)
9
- new(*args, &block).call
10
- end
11
-
12
- attr_accessor :room, :check_in_date, :check_out_date
13
- private :room=, :check_in_date=, :check_out_date=
14
-
15
- def initialize(room, check_in_date:, check_out_date:)
16
- self.room = room
17
- self.check_in_date = check_in_date
18
- self.check_out_date = check_out_date
19
- freeze
20
- end
21
-
22
- def call
23
- HotelBeds::Model::AvailableRoom.new({
24
- id: room.at_css("HotelRoom").attr("SHRUI"),
25
- room_count: room.at_css("HotelOccupancy RoomCount").content,
26
- adult_count: room.at_css("HotelOccupancy AdultCount").content,
27
- child_count: room.at_css("HotelOccupancy ChildCount").content,
28
- number_available: room.at_css("HotelRoom").attr("availCount"),
29
- description: room.at_css("HotelRoom RoomType").content,
30
- board: room.at_css("HotelRoom Board").content,
31
- board_code: room.at_css("HotelRoom Board").attr("code"),
32
- room_type_code: room.at_css("HotelRoom RoomType").attr("code"),
33
- room_type_characteristic: room.at_css("HotelRoom RoomType").attr("characteristic"),
34
- price: total_price,
35
- rates: parse_price_list(room.css("Price PriceList Price"))
36
- })
37
- end
38
-
39
- private
40
- def total_price
41
- raw = ((room.at_css("HotelRoom") > "Price") > "Amount").first.content
42
- BigDecimal.new(raw.to_s)
43
- end
44
-
45
- def parse_price_list(prices)
46
- if prices.empty?
47
- dates = (check_in_date..check_out_date).to_a
48
- amount = total_price / dates.size
49
- dates.inject(Hash.new) do |result, date|
50
- result.merge(date => amount)
51
- end
52
- else
53
- Array(prices).map do |price|
54
- HotelBeds::HotelSearch::Parser::Price.call(price)
55
- end.inject(Hash.new, :merge)
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,101 +0,0 @@
1
- # see INTERNALS.md for comment symbols
2
- module HotelBeds
3
- module HotelSearch
4
- module Parser
5
- class RoomGrouper < Struct.new(:requested_rooms, :response_rooms)
6
- def results
7
- unique_room_combinations
8
- end
9
-
10
- private
11
- def unique_room_combinations
12
- combinations = build_combinations(room_options_grouped_by_occupants)
13
- unique_combinations(expand_combinations(combinations))
14
- end
15
-
16
- def unique_combinations(combinations)
17
- combinations.uniq { |r| r.sort_by(&:id).map(&:id) }
18
- end
19
-
20
- def expand_combinations(combinations)
21
- combinations.map do |rooms|
22
- rooms.inject(Array.new) do |result, room|
23
- # get an array of all the child ages
24
- child_ages = requested_rooms_child_ages_by_occupants.fetch(occupant_key(room))
25
- 1.upto(room.room_count) do |i|
26
- result << room.dup.tap do |r|
27
- r.room_count = 1
28
- r.child_ages = child_ages.pop
29
- end
30
- end
31
- result
32
- end
33
- end
34
- end
35
-
36
- # returns an array of room combinations for all rooms
37
- def build_combinations(combinations)
38
- head, *rest = combinations
39
- Array(head).product(*rest)
40
- end
41
-
42
- # returns a array of arrays, each contains available rooms for a given
43
- # room occupancy
44
- def room_options_grouped_by_occupants
45
- requested_room_count_by_occupants.inject(Array.new) do |result, (key, count)|
46
- result << response_rooms_by_occupants.fetch(key).select do |room|
47
- room.room_count == count
48
- end
49
- end
50
- rescue KeyError => e
51
- Array.new
52
- end
53
-
54
- # returns a hash of OK => [Integer]
55
- def requested_rooms_child_ages_by_occupants
56
- requested_rooms.inject(Hash.new) do |result, room|
57
- key = occupant_key(room)
58
- result[key] ||= Array.new
59
- result[key] += room.child_ages
60
- result
61
- end
62
- end
63
-
64
- # returns a hash of OK => [RR]
65
- def requested_rooms_by_occupants
66
- requested_rooms.inject(Hash.new) do |result, room|
67
- key = occupant_key(room)
68
- result[key] ||= Array.new
69
- result[key] << room
70
- result
71
- end
72
- end
73
-
74
- # returns a hash of OK => 1 (count)
75
- def requested_room_count_by_occupants
76
- requested_rooms_by_occupants.inject(Hash.new) do |result, (key, rooms)|
77
- result.merge(key => rooms.size)
78
- end
79
- end
80
-
81
- # returns a hash of OK => [AvailableRoom, AvailableRoom, AvailableRoom]
82
- def response_rooms_by_occupants
83
- response_rooms.inject(Hash.new) do |result, room|
84
- key = occupant_key(room)
85
- result[key] ||= Array.new
86
- result[key].push(room)
87
- result
88
- end
89
- end
90
-
91
- # returns an OK for a given room
92
- def occupant_key(room)
93
- {
94
- adult_count: room.adult_count,
95
- child_count: room.child_count
96
- }
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,58 +0,0 @@
1
- require "hotel_beds"
2
-
3
- RSpec.describe "adding a hotel to the basket" do
4
- describe "#response" do
5
- before(:all) do
6
- @client = HotelBeds::Client.new({
7
- endpoint: :test,
8
- username: ENV.fetch("HOTEL_BEDS_USERNAME"),
9
- password: ENV.fetch("HOTEL_BEDS_PASSWORD"),
10
- proxy: ENV.fetch("HOTEL_BEDS_PROXY", nil)
11
- })
12
- @check_in_date = Date.today + 28 + rand(10)
13
- @check_out_date = @check_in_date + rand(3) + rand(2) + 1
14
- @search_response = @client.perform_hotel_search({
15
- check_in_date: @check_in_date,
16
- check_out_date: @check_out_date,
17
- rooms: [{ adult_count: 2 }],
18
- destination: "SYD"
19
- }).response
20
-
21
- hotel = @search_response.hotels.first
22
- result = hotel.results.first
23
-
24
- @operation = @client.add_hotel_room_to_basket({
25
- service: {
26
- check_in_date: @check_in_date,
27
- check_out_date: @check_out_date,
28
- availability_token: hotel.availability_token,
29
- hotel_code: hotel.id,
30
- destination_code: hotel.destination_code,
31
- contract_name: hotel.contract_name,
32
- contract_incoming_office_code: hotel.contract_incoming_office_code,
33
- rooms: result.rooms
34
- }
35
- })
36
- if @operation.errors.any?
37
- raise StandardError, @operation.errors.full_messages.join("\n")
38
- end
39
- @response = @operation.response
40
- end
41
-
42
- let(:response) { @response }
43
-
44
- subject { response }
45
-
46
- it "should be a success" do
47
- expect(subject).to be_success
48
- end
49
-
50
- describe "#purchase" do
51
- subject { response.purchase }
52
-
53
- it "should have a service" do
54
- expect(subject.services).to_not be_empty
55
- end
56
- end
57
- end
58
- end
@@ -1,67 +0,0 @@
1
- require "hotel_beds"
2
-
3
- RSpec.describe "performing a grouped hotel search" do
4
- describe "#response" do
5
- before(:all) do
6
- @client = HotelBeds::Client.new({
7
- endpoint: :test,
8
- username: ENV.fetch("HOTEL_BEDS_USERNAME"),
9
- password: ENV.fetch("HOTEL_BEDS_PASSWORD"),
10
- proxy: ENV.fetch("HOTEL_BEDS_PROXY", nil)
11
- })
12
- @check_in_date = Date.today + 28 + rand(10)
13
- @check_out_date = @check_in_date + rand(3) + rand(2) + 1
14
- @operation = @client.perform_hotel_search({
15
- check_in_date: @check_in_date,
16
- check_out_date: @check_out_date,
17
- rooms: [
18
- { adult_count: 2 },
19
- { adult_count: 1, child_count: 1, child_ages: [rand(17)] }
20
- ],
21
- destination: "SYD",
22
- group_results: true
23
- })
24
- if @operation.errors.any?
25
- raise StandardError, @operation.errors.full_messages.join("\n")
26
- end
27
- @response = @operation.response
28
- end
29
-
30
- let(:check_in_date) { @check_in_date }
31
- let(:check_out_date) { @check_out_date }
32
- let(:response) { @response }
33
-
34
- describe "#errors" do
35
- subject { response.errors }
36
-
37
- it "should be empty" do
38
- expect(subject).to be_empty
39
- end
40
- end
41
-
42
- describe "#hotels" do
43
- subject { response.hotels }
44
-
45
- it "should have room counts to match the searched room count" do
46
- room_counts = subject.map { |h| h.results.map { |r| r.rooms.size } }
47
- expect(room_counts.to_a.flatten.uniq).to eq([response.request.rooms.size])
48
- end
49
- end
50
-
51
- describe "#current_page" do
52
- subject { response.current_page }
53
-
54
- it "should return '1'" do
55
- expect(subject).to eq(1)
56
- end
57
- end
58
-
59
- describe "#total_pages" do
60
- subject { response.total_pages }
61
-
62
- it "should be greater or equal to current page" do
63
- expect(subject).to be >= response.current_page
64
- end
65
- end
66
- end
67
- end
@@ -1,113 +0,0 @@
1
- require "hotel_beds"
2
-
3
- RSpec.describe "performing a hotel search" do
4
- describe "#response" do
5
- before(:all) do
6
- @client = HotelBeds::Client.new({
7
- endpoint: :test,
8
- username: ENV.fetch("HOTEL_BEDS_USERNAME"),
9
- password: ENV.fetch("HOTEL_BEDS_PASSWORD"),
10
- proxy: ENV.fetch("HOTEL_BEDS_PROXY", nil)
11
- })
12
- @stay_length_in_days = rand(3) + 1
13
- @check_in_date = Date.today + 28 + rand(10)
14
- @check_out_date = @check_in_date + @stay_length_in_days
15
- @response = @client.perform_hotel_search({
16
- check_in_date: @check_in_date,
17
- check_out_date: @check_out_date,
18
- rooms: [{ adult_count: 2 }],
19
- destination: "SYD"
20
- }).response
21
- end
22
-
23
- let(:check_in_date) { @check_in_date }
24
- let(:check_out_date) { @check_out_date }
25
- let(:stay_length_in_days) { @stay_length_in_days }
26
- let(:response) { @response }
27
-
28
- describe "#errors" do
29
- subject { response.errors }
30
-
31
- it "should be empty" do
32
- expect(subject).to be_empty
33
- end
34
- end
35
-
36
- describe "#hotels" do
37
- subject { response.hotels }
38
-
39
- it "should return an array of the hotels available" do
40
- expect(subject.to_a).to be_kind_of(Array)
41
- expect(subject.to_a).to_not be_empty
42
- expect(subject.first).to be_kind_of(HotelBeds::Model::Hotel)
43
- end
44
-
45
- it "should only have one room per result" do
46
- room_counts = subject.map { |h| h.results.map { |r| r.rooms.size } }
47
- expect(room_counts.to_a.flatten.uniq).to eq([1])
48
- end
49
-
50
- it "should have a destination code" do
51
- subject.each do |hotel|
52
- expect(hotel.destination_code).to be_present
53
- end
54
- end
55
-
56
- it "should have an availability token" do
57
- subject.each do |hotel|
58
- expect(hotel.availability_token).to be_present
59
- end
60
- end
61
-
62
- it "should have a contract name" do
63
- subject.each do |hotel|
64
- expect(hotel.contract_name).to be_present
65
- end
66
- end
67
-
68
- it "should have a contract incoming office code" do
69
- subject.each do |hotel|
70
- expect(hotel.contract_incoming_office_code).to be_present
71
- end
72
- end
73
-
74
- describe "#results" do
75
- describe "#rooms" do
76
- subject do
77
- results = response.hotels.map(&:results).to_a.flatten
78
- results.map(&:rooms).to_a.flatten
79
- end
80
-
81
- it "should parse the rates correctly" do
82
- subject.each do |room|
83
- expect(room.price).to eq(room.rates.values.inject(:+))
84
- end
85
- end
86
-
87
- it "should have the same number of rates as nights requested" do
88
- subject.each do |room|
89
- expect(room.rates).to_not be_empty
90
- expect(room.rates.size).to eq(stay_length_in_days)
91
- end
92
- end
93
- end
94
- end
95
- end
96
-
97
- describe "#current_page" do
98
- subject { response.current_page }
99
-
100
- it "should return '1'" do
101
- expect(subject).to eq(1)
102
- end
103
- end
104
-
105
- describe "#total_pages" do
106
- subject { response.total_pages }
107
-
108
- it "should be greater or equal to current page" do
109
- expect(subject).to be >= response.current_page
110
- end
111
- end
112
- end
113
- end