Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions drivers/place/room_booking_approval.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ class Place::RoomBookingApproval < PlaceOS::Driver

default_settings({
check_recurring_event_id: false, # fetches the event to verify the provided id is the series root.
check_bookings_every_minutes: 5,
check_bookings_every_minutes: 2,
refresh_debounce_seconds: 10, # seconds to wait before triggering Bookings poll after an approval action.
disable_refresh_bookings: false, # set to true to skip the debounced Bookings re-poll entirely.
})

accessor calendar : Calendar_1

@check_recurring_event_id : Bool = false
@booking_poll_rate : UInt32 = 5
@booking_poll_rate : UInt32 = 2
@refresh_debounce : UInt32 = 10
@disable_refresh_bookings : Bool = false
@pending_refresh : Set(String) = Set(String).new
@refresh_scheduled : Bool = false

# ameba:disable Lint/NotNil
getter building_id : String { get_building_id.not_nil! }
Expand All @@ -25,9 +31,13 @@ class Place::RoomBookingApproval < PlaceOS::Driver
@building_id = nil
@systems = nil
@check_recurring_event_id = setting?(Bool, :check_recurring_event_id) || false
@booking_poll_rate = setting?(UInt32, :check_bookings_every_minutes) || 5_u32
@booking_poll_rate = setting?(UInt32, :check_bookings_every_minutes) || 2_u32
@refresh_debounce = setting?(UInt32, :refresh_debounce_seconds) || 10_u32
@disable_refresh_bookings = setting?(Bool, :disable_refresh_bookings) || false

schedule.clear
@pending_refresh.clear
@refresh_scheduled = false
# used to detect changes in building configuration
schedule.every(1.hour) { @systems = get_systems_list.not_nil! } # ameba:disable Lint/NotNil

Expand Down Expand Up @@ -93,32 +103,78 @@ class Place::RoomBookingApproval < PlaceOS::Driver

@[Security(Level::Support)]
def accept_event(calendar_id : String, event_id : String, user_id : String? = nil, notify : Bool = true, comment : String? = nil)
affected_systems = find_affected_systems(event_id)
calendar.accept_event(calendar_id: calendar_id, event_id: event_id, user_id: user_id, notify: notify, comment: comment)
clear_cache(event_id: event_id)
refresh_bookings(affected_systems)
end

@[Security(Level::Support)]
def accept_recurring_event(calendar_id : String, recurring_event_id : String, user_id : String? = nil, notify : Bool = true, comment : String? = nil)
recurring_event_id = resolve_recurring_event_id(calendar_id, recurring_event_id, user_id) if @check_recurring_event_id

logger.debug { "accepting recurring event #{recurring_event_id} on #{calendar_id}" }
affected_systems = find_affected_systems(recurring_event_id)
calendar.accept_event(calendar_id: calendar_id, event_id: recurring_event_id, user_id: user_id, notify: notify, comment: comment)
clear_cache(event_id: recurring_event_id)
refresh_bookings(affected_systems)
end

@[Security(Level::Support)]
def decline_event(calendar_id : String, event_id : String, user_id : String? = nil, notify : Bool = true, comment : String? = nil)
affected_systems = find_affected_systems(event_id)
calendar.decline_event(calendar_id: calendar_id, event_id: event_id, user_id: user_id, notify: notify, comment: comment)
clear_cache(event_id: event_id)
refresh_bookings(affected_systems)
end

@[Security(Level::Support)]
def decline_recurring_event(calendar_id : String, recurring_event_id : String, user_id : String? = nil, notify : Bool = true, comment : String? = nil)
recurring_event_id = resolve_recurring_event_id(calendar_id, recurring_event_id, user_id) if @check_recurring_event_id

logger.debug { "declining recurring event #{recurring_event_id} on #{calendar_id}" }
affected_systems = find_affected_systems(recurring_event_id)
calendar.decline_event(calendar_id: calendar_id, event_id: recurring_event_id, user_id: user_id, notify: notify, comment: comment)
clear_cache(event_id: recurring_event_id)
refresh_bookings(affected_systems)
end

# Returns the system IDs whose cached tentative events match the given
# event_id (by event id or recurring_event_id). Must be called *before*
# clear_cache so the cache still contains the events.
protected def find_affected_systems(event_id : String) : Array(String)
cache = self[:approval_required]? ? ApprovalCache.from_json(self[:approval_required].to_json) : ApprovalCache.new
cache.compact_map do |system_id, events|
system_id if events.any? { |event| event.id == event_id || event.recurring_event_id == event_id }
end
end

# Queues the given system IDs for a debounced Bookings re-poll.
# Multiple calls within the 10-second window are batched into a
# single poll_events call per affected system, giving the calendar
# provider time to propagate the status change.
protected def refresh_bookings(system_ids : Array(String))
return if @disable_refresh_bookings
@pending_refresh.concat(system_ids)
return if @refresh_scheduled
@refresh_scheduled = true
schedule.in(@refresh_debounce.seconds) { perform_refresh }
end

# Drains the pending set and triggers poll_events on each system.
protected def perform_refresh
system_ids = @pending_refresh.dup
@pending_refresh.clear
@refresh_scheduled = false

system_ids.each do |system_id|
begin
sys = system(system_id)
sys.get("Bookings", 1).poll_events if sys.exists?("Bookings", 1)
rescue error
logger.warn(exception: error) { "unable to trigger poll_events on Bookings in #{system_id}" }
end
end
end

@[Security(Level::Support)]
Expand Down
Loading
Loading