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
@@ -0,0 +1,14 @@
1
+ require "hotel_beds/parser"
2
+
3
+ module HotelBeds
4
+ module Parser
5
+ class Price
6
+ include HotelBeds::Parser
7
+
8
+ # attributes
9
+ attribute :from, selector: "DateTimeFrom", attr: "date"
10
+ attribute :to, selector: "DateTimeTo", attr: "date"
11
+ attribute :amount, selector: "Amount"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ require "hotel_beds/parser"
2
+ require "hotel_beds/parser/service"
3
+ require "hotel_beds/parser/customer"
4
+
5
+ module HotelBeds
6
+ module Parser
7
+ class Purchase
8
+ include HotelBeds::Parser
9
+
10
+ # attributes
11
+ attribute :token, attr: "purchaseToken"
12
+ attribute :time_to_expiration, attr: "timeToExpiration"
13
+ attribute :agency_reference, selector: "AgencyReference"
14
+ attribute :status, selector: "Status"
15
+ attribute :currency, selector: "Currency", attr: "code"
16
+ attribute :amount, selector: "TotalPrice"
17
+ attribute :services,
18
+ selector: "ServiceList > Service", multiple: true,
19
+ parser: HotelBeds::Parser::Service
20
+ attribute :holder, selector: "Holder",
21
+ parser: HotelBeds::Parser::Customer
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,104 @@
1
+ # see INTERNALS.md for comment symbols
2
+ module HotelBeds
3
+ module Parser
4
+ class RoomGrouper
5
+ attr_accessor :requested_rooms, :response_rooms
6
+ private :requested_rooms=, :response_rooms=
7
+
8
+ def initialize(requested_rooms, response_rooms)
9
+ self.requested_rooms = requested_rooms
10
+ self.response_rooms = response_rooms
11
+ freeze
12
+ end
13
+
14
+ def groups
15
+ combinations = build_combinations(room_options_grouped_by_occupants)
16
+ unique_combinations(expand_combinations(combinations))
17
+ end
18
+
19
+ private
20
+ def unique_combinations(combinations)
21
+ combinations.uniq { |r| r.sort_by(&:id).map(&:id) }
22
+ end
23
+
24
+ def expand_combinations(combinations)
25
+ combinations.map do |rooms|
26
+ rooms.inject(Array.new) do |result, room|
27
+ # get an array of all the child ages
28
+ child_ages = requested_rooms_child_ages_by_occupants.fetch(occupant_key(room))
29
+ 1.upto(room.room_count) do |i|
30
+ result << room.dup.tap do |r|
31
+ r.room_count = 1
32
+ r.child_ages = child_ages.pop
33
+ end
34
+ end
35
+ result
36
+ end
37
+ end
38
+ end
39
+
40
+ # returns an array of room combinations for all rooms
41
+ def build_combinations(combinations)
42
+ head, *rest = combinations
43
+ Array(head).product(*rest)
44
+ end
45
+
46
+ # returns a array of arrays, each contains available rooms for a given
47
+ # room occupancy
48
+ def room_options_grouped_by_occupants
49
+ requested_room_count_by_occupants.inject(Array.new) do |result, (key, count)|
50
+ result << response_rooms_by_occupants.fetch(key).select do |room|
51
+ room.room_count == count
52
+ end
53
+ end
54
+ rescue KeyError => e
55
+ Array.new
56
+ end
57
+
58
+ # returns a hash of OK => [Integer]
59
+ def requested_rooms_child_ages_by_occupants
60
+ requested_rooms.inject(Hash.new) do |result, room|
61
+ key = occupant_key(room)
62
+ result[key] ||= Array.new
63
+ result[key] += room.child_ages
64
+ result
65
+ end
66
+ end
67
+
68
+ # returns a hash of OK => [RR]
69
+ def requested_rooms_by_occupants
70
+ requested_rooms.inject(Hash.new) do |result, room|
71
+ key = occupant_key(room)
72
+ result[key] ||= Array.new
73
+ result[key] << room
74
+ result
75
+ end
76
+ end
77
+
78
+ # returns a hash of OK => 1 (count)
79
+ def requested_room_count_by_occupants
80
+ requested_rooms_by_occupants.inject(Hash.new) do |result, (key, rooms)|
81
+ result.merge(key => rooms.size)
82
+ end
83
+ end
84
+
85
+ # returns a hash of OK => [AvailableRoom, AvailableRoom, AvailableRoom]
86
+ def response_rooms_by_occupants
87
+ response_rooms.inject(Hash.new) do |result, room|
88
+ key = occupant_key(room)
89
+ result[key] ||= Array.new
90
+ result[key].push(room)
91
+ result
92
+ end
93
+ end
94
+
95
+ # returns an OK for a given room
96
+ def occupant_key(room)
97
+ {
98
+ adult_count: room.adult_count,
99
+ child_count: room.child_count
100
+ }
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,21 @@
1
+ require "hotel_beds/parser"
2
+ require "hotel_beds/parser/contract"
3
+
4
+ module HotelBeds
5
+ module Parser
6
+ class Service
7
+ include HotelBeds::Parser
8
+
9
+ # attributes
10
+ attribute :id, attr: "SPUI"
11
+ attribute :type, attr: "xsi:type"
12
+ attribute :status, selector: "Status"
13
+ attribute :date_from, selector: "DateFrom", attr: "date"
14
+ attribute :date_to, selector: "DateTo", attr: "date"
15
+ attribute :currency, selector: "Currency", attr: "code"
16
+ attribute :amount, selector: "TotalAmount"
17
+ attribute :contract, selector: "Contract",
18
+ parser: HotelBeds::Parser::Contract
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ require "delegate"
2
+
3
+ module HotelBeds
4
+ module PurchaseConfirm
5
+ class Envelope < SimpleDelegator
6
+ def attributes
7
+ {
8
+ Language: language,
9
+ ConfirmationData: {
10
+ :"@purchaseToken" => purchase.token,
11
+ :Holder => customer(purchase.holder),
12
+ :AgencyReference => purchase.agency_reference,
13
+ :ConfirmationServiceDataList => {
14
+ :ServiceData => purchase.services.map(&method(:service_data))
15
+ }
16
+ }
17
+ }
18
+ end
19
+
20
+ private
21
+ def service_data(service)
22
+ {
23
+ :@SPUI => service.id,
24
+ :"@xsi:type" => "ConfirmationServiceDataHotel",
25
+ :CustomerList => {
26
+ :Customer => service.customers.map(&method(:customer))
27
+ }
28
+ }
29
+ end
30
+
31
+ def customer(customer)
32
+ {
33
+ :@type => (:child == customer.type ? "CH" : "AD"),
34
+ :CustomerId => customer.id,
35
+ :Age => customer.age,
36
+ :Name => customer.name,
37
+ :LastName => customer.last_name,
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ require_relative "request"
2
+ require_relative "response"
3
+ require_relative "envelope"
4
+
5
+ module HotelBeds
6
+ module PurchaseConfirm
7
+ class Operation
8
+ attr_accessor :request, :response, :errors
9
+ private :request=, :response=, :errors=
10
+
11
+ def initialize(*args)
12
+ self.request = Request.new(*args)
13
+ end
14
+
15
+ def perform(connection:)
16
+ if request.valid?
17
+ self.response = Response.new(request, retrieve(connection))
18
+ self.errors = response.errors
19
+ else
20
+ self.errors = request.errors
21
+ end
22
+ freeze
23
+ self
24
+ end
25
+
26
+ private
27
+ def retrieve(connection)
28
+ connection.call({
29
+ method: :purchaseConfirm,
30
+ namespace: :PurchaseConfirmRQ,
31
+ data: Envelope.new(request).attributes
32
+ })
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,72 @@
1
+ require "securerandom"
2
+ require "hotel_beds/model"
3
+ require "hotel_beds/model/customer"
4
+
5
+ module HotelBeds
6
+ module PurchaseConfirm
7
+ class Request
8
+ class Service
9
+ include HotelBeds::Model
10
+
11
+ # attributes
12
+ attribute :id, String
13
+ attribute :type, String
14
+ attribute :customers, Array[HotelBeds::Model::Customer]
15
+
16
+ # validation
17
+ validates :customers, length: { minimum: 1 }
18
+ validates :id, :type, presence: true
19
+ validate do |service|
20
+ unless service.customers.all?(&:valid?)
21
+ service.errors.add(:customers, "are invalid")
22
+ end
23
+ end
24
+ end
25
+
26
+ class Purchase
27
+ include HotelBeds::Model
28
+
29
+ # attributes
30
+ attribute :token, String
31
+ attribute :holder, HotelBeds::Model::Customer
32
+ attribute :agency_reference, String
33
+ attribute :services, Array[Service]
34
+
35
+ # validation
36
+ validates :services, length: { minimum: 1 }
37
+ validates :token, :agency_reference, :holder, presence: true
38
+ validate do |purchase|
39
+ purchase.services.each do |service|
40
+ unless service.valid?
41
+ service.errors.full_messages.each do |message|
42
+ purchase.errors.add(:services, message)
43
+ end
44
+ end
45
+ end
46
+ if purchase.holder && purchase.holder.invalid?
47
+ purchase.holder.errors.full_messages.each do |message|
48
+ purchase.errors.add(:holder, message)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ include HotelBeds::Model
55
+
56
+ # attributes
57
+ attribute :language, String, default: "ENG"
58
+ attribute :purchase, Purchase
59
+
60
+ # validation
61
+ validates :language, length: { is: 3 }
62
+ validates :purchase, presence: true
63
+ validate do |request|
64
+ if request.purchase && request.purchase.invalid?
65
+ request.purchase.errors.full_messages.each do |message|
66
+ request.errors.add(:purchase, message)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ require "hotel_beds/parser/errors"
2
+ require "hotel_beds/parser/purchase"
3
+
4
+ module HotelBeds
5
+ module PurchaseConfirm
6
+ class Response
7
+ attr_accessor :request, :headers, :body, :errors
8
+ private :request=, :headers=, :body=, :errors=
9
+
10
+ def initialize(request, response)
11
+ self.request = request
12
+ self.headers = response.header
13
+ self.body = Nokogiri::XML(response.body.fetch(:purchase_confirm))
14
+ self.errors = HotelBeds::Parser::Errors.new(response).to_model(self)
15
+ freeze
16
+ end
17
+
18
+ def inspect
19
+ "<#{self.class.name} errors=#{errors.inspect} headers=#{headers.inspect} body=#{body.to_s}>"
20
+ end
21
+
22
+ def success?
23
+ errors.empty?
24
+ end
25
+
26
+ def purchase
27
+ HotelBeds::Parser::Purchase.new(body.at_css("Purchase")).to_model
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ require "delegate"
2
+
3
+ module HotelBeds
4
+ module PurchaseFlush
5
+ class Envelope < SimpleDelegator
6
+ def attributes
7
+ {
8
+ :Language => language,
9
+ :@version => "2013/12",
10
+ :@purchaseToken => purchase_token
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ require_relative "request"
2
+ require_relative "response"
3
+ require_relative "envelope"
4
+
5
+ module HotelBeds
6
+ module PurchaseFlush
7
+ class Operation
8
+ attr_accessor :request, :response, :errors
9
+ private :request=, :response=, :errors=
10
+
11
+ def initialize(*args)
12
+ self.request = Request.new(*args)
13
+ end
14
+
15
+ def perform(connection:)
16
+ if request.valid?
17
+ self.response = Response.new(request, retrieve(connection))
18
+ self.errors = response.errors
19
+ else
20
+ self.errors = request.errors
21
+ end
22
+ freeze
23
+ self
24
+ end
25
+
26
+ private
27
+ def retrieve(connection)
28
+ connection.call({
29
+ method: :purchaseFlush,
30
+ namespace: :PurchaseFlushRQ,
31
+ data: Envelope.new(request).attributes
32
+ })
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ require "hotel_beds/model"
2
+
3
+ module HotelBeds
4
+ module PurchaseFlush
5
+ class Request
6
+ include HotelBeds::Model
7
+
8
+ # attributes
9
+ attribute :language, String, default: "ENG"
10
+ attribute :purchase_token, String
11
+
12
+ # validation
13
+ validates :language, length: { is: 3 }
14
+ validates :purchase_token, presence: true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require "hotel_beds/parser/errors"
2
+
3
+ module HotelBeds
4
+ module PurchaseFlush
5
+ class Response
6
+ attr_accessor :request, :headers, :body, :errors
7
+ private :request=, :headers=, :body=, :errors=
8
+
9
+ def initialize(request, response)
10
+ self.request = request
11
+ self.headers = response.header
12
+ self.body = Nokogiri::XML(response.body.fetch(:purchase_flush))
13
+ self.errors = HotelBeds::Parser::Errors.new(response).to_model(self)
14
+ freeze
15
+ end
16
+
17
+ def inspect
18
+ "<#{self.class.name} errors=#{errors.inspect} headers=#{headers.inspect} body=#{body.to_s}>"
19
+ end
20
+
21
+ def success?
22
+ errors.empty? && body.at_css("Status").content == "Y"
23
+ end
24
+ end
25
+ end
26
+ end