-
Notifications
You must be signed in to change notification settings - Fork 0
Synchronising the calendar of availabilities
Supplier implementations typically contain two parts: synchronising the properties and their information (title, descriptions, images); and synchronising the calendar of availabilities, which indicate what dates are available or not and what the rates are.
These activities are represented by the two kinds of workers supplier implementations are able to provide: metadata
and availabilities
. The former is documented in another Wiki page - it is suggested that that page is read before this one, since it includes more extensive information related to the way synchronising is supposed to work on Concierge; this page, on the other hand, is focused solely on the process of synchronising the calendar of availabilities for a given property on Concierge.
# apps/workers/suppliers/acme/availabilities.rb
module Workers::Suppliers::Acme
class Availabilities
attr_reader :synchronisation, :host
def initialize(host)
@host = host
@synchronisation = Workers::CalendarSynchronisation.new(host)
end
def perform
properties = properties_from(host)
properties.each do |property|
synchronisation.start(property.identifier) do
calendar = Roomorama::Calendar.new(property.identifier)
dates = client.fetch_calendar(property.identifier)
dates.each do |data|
entry = Roomorama::Calendar::Entry.new(
date: data["date"],
available: data["available"] == 1,
nightly_rate: data["price"]
)
calendar.add(entry)
end
Result.new(calendar)
end
end
synchronisation.finish!
end
private
def properties_from(host)
PropertyRepository.from_host(host)
end
def client
@client ||= Acme::Client.new(credentials)
end
def credentials
@credentials ||= Concierge::Credentials.for("acme")
end
end
end
Concierge::Announcer.on("availabilities.Acme") do |host|
Workers::Suppliers::Acme::Availabilities.new(host).perform
end
Let's analyse the code above step by step.
def initialize(host)
@host = host
@synchronisation = Workers::CalendarSynchronisation.new(host)
end
This is very similar to what is done in the initialization of the metadata worker. An instance of Host
is received. However, this time the supporting core class from Concierge is Workers::CalendarSynchronisation
. A similar interface is exposed, which makes the process more seamless.
def perform
properties = properties_from(host)
# ...
def properties_from(host)
PropertyRepository.from_host(host)
end
At the first line of the perform
method, all properties from the given host are loaded from the database. This step is important and ensures that only previously synchronised properties have their calendars fetched from the supplier.
Important: As a rule of thumb, calendar implementations should scope their calendar calls only to properties previously synchronised (i.e., properties in the properties
table.) This can substantially speed up the synchronisation process, as well as reduce the number of network requests to supplier APIs, which typically impose some form of rate limitting.
properties.each do |property|
synchronisation.start(property.identifier) do
calendar = Roomorama::Calendar.new(property.identifier)
dates = client.fetch_calendar(property.identifier)
This time, the code iterates over each property previously sychronised by Concierge for this host. For each one of them a Roomorama::Calendar
instance is created. As with Workers::PropertySynchronisation
, the Workers::CalendarSynchronisation
class also expects an instance of Result
to be returned - however, the result should wrap a Roomorama::Calendar
when successful.
The fetch_calendar
is a ficticious method which is supposed to download the calendar of availabilities for a given property.
dates.each do |data|
entry = Roomorama::Calendar::Entry.new(
date: data["date"],
available: data["available"] == 1,
nightly_rate: data["price"]
)
calendar.add(entry)
end
An entry (that is, an instance of Roomorama::Calendar::Entry
) represents a single date in the availabilities calendar. It groups the date, whether or not it is available, and what the rates are (see the class' documentation for a list of supported fields.)
At the end of the process, the entry is added to the calendar, meaning it will be sent to Roomorama via API.
Result.new(calendar)
As was mentioned previously, the Workers::CalendarSynchronisation#start
method expects an instance of Result
to be returned back, and it should wrap the corresponding Roomorama::Calendar
instance when successful.
Concierge::Announcer.on("availabilities.Acme") do |host|
Workers::Suppliers::Acme::Availabilities.new(host).perform
end
Supplier implementations are expected to listen for the availabilities.<supplier_name>
event. When it is triggered, an instance of Host
is passed, and the calendar of availabilities for all properties of that host should be sycnhronised.
-
In case the block passed to
Workers::CalendarSynchronisation#start
returns an unsuccessfulResult
, the error is propertly stored to the database, along with the context. No error saving is necessary after the method is called, therefore, if this practice is followed. -
There is multi-unit support for calendar synchronisation. The
Roomorama::Calendar
class has aadd_unit
method, which receives another instance ofRoomorama::Calendar
, representing the availabilities for one of the units of the parent property. -
If the supplier does not provide a breakdown of prices per date, but only stays (price for check-in/check-out combination), then the support
Roomorama::Calendar::StaysMapper
class can be used to automatically generate a valid set ofRoomorama::Calendar::Entry
objects. See the documentation of that class for more information.