Skip to content

Commit

Permalink
fix(at_capacity_mailer): #check_capacity (#541)
Browse files Browse the repository at this point in the history
* fix(at_capacity_mailer): #check_capacity

* test(at_capacity_mailer): test AtCapacityMailer

* test(at_capacity_mailer): driver name

* refactor(at_capacity_mailer): simplify driver and handle new locker metadata format
  • Loading branch information
chillfox authored Feb 18, 2025
1 parent 96888bf commit d684dbb
Show file tree
Hide file tree
Showing 2 changed files with 331 additions and 15 deletions.
22 changes: 7 additions & 15 deletions drivers/place/at_capacity_mailer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ class Place::AtCapacityMailer < PlaceOS::Driver
booked_asset_ids = get_booked_asset_ids

@zones.each do |zone_id|
next if asset_ids[zone_id].empty?
next unless (zone_asset_ids = asset_ids[zone_id]?) && !zone_asset_ids.empty?

if (asset_ids[zone_id] - [booked_asset_ids]).empty?
if (zone_asset_ids - booked_asset_ids).empty?
logger.debug { "zone #{zone_id} is at capacity" }
send_email(zone_id)
end
Expand All @@ -97,20 +97,16 @@ class Place::AtCapacityMailer < PlaceOS::Driver
assets_ids = {} of String => Array(String)

@zones.each do |zone_id|
begin
assets_ids[zone_id] = get_assets_from_metadata(@booking_type, zone_id).map { |asset| asset.id }.uniq!
rescue error
logger.warn(exception: error) { "unable to get #{@booking_type} assets from zone #{zone_id} metadata" }
end
assets_ids[zone_id] = get_assets_from_metadata(zone_id).map { |asset| asset.id }.uniq!
end

self[:assets_ids] = assets_ids
end

def get_assets_from_metadata(type : String, zone_id : String) : Array(Asset)
def get_assets_from_metadata(zone_id : String) : Array(Asset)
assets = [] of Asset

metadata_field = case type
metadata_field = case @booking_type
when "desk"
"desks"
when "parking"
Expand All @@ -121,16 +117,12 @@ class Place::AtCapacityMailer < PlaceOS::Driver

if metadata_field
metadata = Metadata.from_json staff_api.metadata(zone_id, metadata_field).get[metadata_field].to_json
if "lockers"
assets = metadata.details.as_a.flat_map { |locker_bank| locker_bank.as_h["lockers"].as_a.map { |locker| Asset.from_json locker.to_json } }
else
assets = metadata.details.as_a.map { |asset| Asset.from_json asset.to_json }
end
assets = metadata.details.as_a.map { |asset| Asset.from_json asset.to_json }
end

assets
rescue error
logger.warn(exception: error) { "unable to get #{type} assets from zone #{zone_id} metadata" }
logger.warn(exception: error) { "unable to get #{metadata_field} from zone #{zone_id} metadata" }
[] of Asset
end

Expand Down
324 changes: 324 additions & 0 deletions drivers/place/at_capacity_mailer_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
require "placeos-driver/spec"
require "placeos-driver/interface/mailer"

class StaffAPI < DriverSpecs::MockDriver
ZONES = [
{
created_at: 1660537814,
updated_at: 1681800971,
id: "level-1",
name: "Level 1",
display_name: "Level 1",
location: "",
description: "",
code: "",
type: "",
count: 0,
capacity: 0,
map_id: "",
tags: [
"level",
],
triggers: [] of String,
parent_id: "zone-0000",
timezone: "Australia/Sydney",
},
{
created_at: 1660537814,
updated_at: 1681800971,
id: "level-2",
name: "Level 2",
display_name: "Level 2",
location: "",
description: "",
code: "",
type: "",
count: 0,
capacity: 0,
map_id: "",
tags: [
"level",
],
triggers: [] of String,
parent_id: "zone-0000",
timezone: "Australia/Sydney",
},
]

def zone(zone_id : String)
zones = ZONES.select { |z| z["id"] == zone_id }
JSON.parse(zones.to_json)
end

def metadata(id : String, key : String? = nil)
zone = ZONES.find! { |z| z["id"] == id }
key = key.not_nil!

details = case key
when "desks"
[
{
"id": "desk-1",
"name": "Desk 1",
"images": [] of String,
"bookable": true,
"features": [] of String,
},
{
"id": "desk-2",
"name": "Desk 2",
"images": [] of String,
"bookable": true,
"features": [] of String,
},
]
when "parking-spaces"
[
{
"id": "park-1",
"name": "Bay 1",
"zone": zone[:id],
"notes": "",
"map_id": "",
"zone_id": zone[:id],
"assigned_to": nil,
"map_rotation": 0,
"assigned_name": nil,
"assigned_user": nil,
},
{
"id": "park-2",
"name": "Bay 2",
"zone": zone[:id],
"notes": "",
"map_id": "",
"zone_id": zone[:id],
"assigned_to": nil,
"map_rotation": 0,
"assigned_name": nil,
"assigned_user": nil,
},
]
when "lockers"
[
{
"id": "locker-1",
"name": "L1-01",
"size": [
1,
3,
],
"tags": [
"Base",
],
"notes": "",
"bank_id": "bank-1",
"bookable": true,
"features": [] of String,
"position": [
0,
0,
],
"accessible": true,
"assigned_user": nil,
},
{
"id": "locker-2",
"name": "L1-02",
"size": [
1,
3,
],
"tags": [
"Base",
],
"notes": "",
"bank_id": "bank-1",
"bookable": true,
"features": [] of String,
"position": [
1,
0,
],
"accessible": true,
"assigned_user": nil,
},
]
end

JSON.parse(
{key => {
name: key,
description: "#{key} for zone #{id}",
details: details,
parent_id: zone[:parent_id],
editors: [] of String,
modified_by_id: "user-1234",
}}.to_json)
end

def booked(
type : String? = nil,
period_start : Int64? = nil,
period_end : Int64? = nil,
zones : Array(String) = [] of String,
user : String? = nil,
email : String? = nil,
state : String? = nil,
event_id : String? = nil,
ical_uid : String? = nil,
created_before : Int64? = nil,
created_after : Int64? = nil,
approved : Bool? = nil,
checked_in : Bool? = nil,
include_checked_out : Bool? = nil,
include_booked_by : Bool? = nil,
department : String? = nil,
limit : Int32? = nil,
offset : Int32? = nil,
permission : String? = nil,
extension_data : JSON::Any? = nil,
)
assets = case type
when "desk"
["desk-1", "desk-2"]
when "parking"
["park-1"]
when "locker"
["locker-1", "locker-2"]
end
JSON.parse(assets.to_json)
end
end

class Mailer < DriverSpecs::MockDriver
include PlaceOS::Driver::Interface::Mailer

def on_load
self[:sent] = 0
end

def send_template(
to : String | Array(String),
template : Tuple(String, String),
args : TemplateItems,
resource_attachments : Array(ResourceAttachment) = [] of ResourceAttachment,
attachments : Array(Attachment) = [] of Attachment,
cc : String | Array(String) = [] of String,
bcc : String | Array(String) = [] of String,
from : String | Array(String) | Nil = nil,
reply_to : String | Array(String) | Nil = nil
)
self[:sent] = self[:sent].as_i + 1
end

def send_mail(
to : String | Array(String),
subject : String,
message_plaintext : String? = nil,
message_html : String? = nil,
resource_attachments : Array(ResourceAttachment) = [] of ResourceAttachment,
attachments : Array(Attachment) = [] of Attachment,
cc : String | Array(String) = [] of String,
bcc : String | Array(String) = [] of String,
from : String | Array(String) | Nil = nil,
reply_to : String | Array(String) | Nil = nil
) : Bool
true
end
end

DriverSpecs.mock_driver "Place::AtCapacityMailer" do
system({
StaffAPI: {StaffAPI},
Mailer: {Mailer},
})

# Start of tests for: #get_booked_asset_ids
###########################################

settings({
booking_type: "parking",
zones: ["level-1"],
})

resp = exec(:get_booked_asset_ids).get
resp.not_nil!.as_a.should eq ["park-1"]

###########################################
# End of tests for: #get_booked_asset_ids

# Start of tests for: #get_assets_from_metadata
###############################################

settings({
booking_type: "desk",
zones: ["level-1"],
})

resp = exec(:get_assets_from_metadata, "level-1").get
resp.not_nil!.as_a.map(&.as_h["id"]).should eq ["desk-1", "desk-2"]

settings({
booking_type: "parking",
zones: ["level-1"],
})

resp = exec(:get_assets_from_metadata, "level-1").get
resp.not_nil!.as_a.map(&.as_h["id"]).should eq ["park-1", "park-2"]

settings({
booking_type: "locker",
zones: ["level-1"],
})

resp = exec(:get_assets_from_metadata, "level-1").get
resp.not_nil!.as_a.map(&.as_h["id"]).should eq ["locker-1", "locker-2"]

###############################################
# End of tests for: #get_assets_from_metadata

# Start of tests for: #get_asset_ids
####################################

settings({
booking_type: "desk",
zones: ["level-1"],
})

resp = exec(:get_asset_ids).get
resp.not_nil!.as_h.should eq Hash{"level-1" => ["desk-1", "desk-2"]}

####################################
# End of tests for: #get_asset_ids

# Start of tests for: #check_capacity
#####################################

# Not fully booked
settings({
booking_type: "parking",
zones: ["level-1"],
})
_resp = exec(:get_asset_ids).get # asset_ids are cached

resp = exec(:check_capacity).get
system(:Mailer_1)[:sent].should eq 0

# Fully booked
settings({
booking_type: "locker",
zones: ["level-1"],
})
_resp = exec(:get_asset_ids).get # asset_ids are cached

resp = exec(:check_capacity).get
system(:Mailer_1)[:sent].should eq 1

# spam protection
resp = exec(:check_capacity).get
system(:Mailer_1)[:sent].should eq 1

#####################################
# End of tests for: #check_capacity
end

0 comments on commit d684dbb

Please sign in to comment.