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.
- checksums.yaml +4 -4
- data/README.md +4 -44
- data/lib/hotel_beds/basket_remove/envelope.rb +16 -0
- data/lib/hotel_beds/basket_remove/operation.rb +36 -0
- data/lib/hotel_beds/basket_remove/request.rb +19 -0
- data/lib/hotel_beds/basket_remove/response.rb +31 -0
- data/lib/hotel_beds/client.rb +21 -0
- data/lib/hotel_beds/hotel_basket_add/request.rb +1 -1
- data/lib/hotel_beds/hotel_basket_add/response.rb +4 -35
- data/lib/hotel_beds/hotel_search/envelope.rb +10 -9
- data/lib/hotel_beds/hotel_search/request.rb +6 -4
- data/lib/hotel_beds/hotel_search/response.rb +4 -9
- data/lib/hotel_beds/model/available_room.rb +22 -1
- data/lib/hotel_beds/model/cancellation_policy.rb +0 -1
- data/lib/hotel_beds/model/comment.rb +13 -0
- data/lib/hotel_beds/model/contract.rb +15 -0
- data/lib/hotel_beds/model/customer.rb +21 -0
- data/lib/hotel_beds/model/destination.rb +13 -0
- data/lib/hotel_beds/model/hotel.rb +12 -7
- data/lib/hotel_beds/model/price.rb +18 -0
- data/lib/hotel_beds/model/purchase.rb +7 -2
- data/lib/hotel_beds/model/room.rb +0 -1
- data/lib/hotel_beds/model/service.rb +6 -4
- data/lib/hotel_beds/parser.rb +94 -0
- data/lib/hotel_beds/parser/available_room.rb +37 -0
- data/lib/hotel_beds/parser/cancellation_policy.rb +13 -0
- data/lib/hotel_beds/parser/comment.rb +13 -0
- data/lib/hotel_beds/parser/contract.rb +17 -0
- data/lib/hotel_beds/parser/customer.rb +18 -0
- data/lib/hotel_beds/parser/destination.rb +13 -0
- data/lib/hotel_beds/parser/errors.rb +39 -0
- data/lib/hotel_beds/parser/hotel.rb +27 -0
- data/lib/hotel_beds/parser/price.rb +14 -0
- data/lib/hotel_beds/parser/purchase.rb +24 -0
- data/lib/hotel_beds/parser/room_grouper.rb +104 -0
- data/lib/hotel_beds/parser/service.rb +21 -0
- data/lib/hotel_beds/purchase_confirm/envelope.rb +42 -0
- data/lib/hotel_beds/purchase_confirm/operation.rb +36 -0
- data/lib/hotel_beds/purchase_confirm/request.rb +72 -0
- data/lib/hotel_beds/purchase_confirm/response.rb +31 -0
- data/lib/hotel_beds/purchase_flush/envelope.rb +15 -0
- data/lib/hotel_beds/purchase_flush/operation.rb +36 -0
- data/lib/hotel_beds/purchase_flush/request.rb +17 -0
- data/lib/hotel_beds/purchase_flush/response.rb +26 -0
- data/lib/hotel_beds/version.rb +1 -1
- data/spec/features/flushing_the_basket_spec.rb +63 -0
- data/spec/features/ordering_a_hotel_room_spec.rb +96 -0
- data/spec/features/removing_a_basket_item_spec.rb +74 -0
- data/spec/lib/hotel_beds/hotel_search/request_spec.rb +4 -4
- data/spec/lib/hotel_beds/{hotel_search/parser → parser}/room_grouper_spec.rb +3 -3
- data/spec/lib/hotel_beds/parser/service_spec.rb +44 -0
- metadata +42 -15
- data/lib/hotel_beds/hotel_search/parser/errors.rb +0 -49
- data/lib/hotel_beds/hotel_search/parser/hotel.rb +0 -71
- data/lib/hotel_beds/hotel_search/parser/price.rb +0 -38
- data/lib/hotel_beds/hotel_search/parser/room.rb +0 -61
- data/lib/hotel_beds/hotel_search/parser/room_grouper.rb +0 -101
- data/spec/features/adding_a_hotel_to_basket_spec.rb +0 -58
- data/spec/features/grouped_hotel_search_spec.rb +0 -67
- 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,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
|