Skip to content
Merged
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
15 changes: 13 additions & 2 deletions .ameba.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ Documentation/DocumentationAdmonition:
Enabled: false

Style/MultilineStringLiteral:
Excluded:
- spec/**/*
Enabled: false

Lint/BeTruthy:
Enabled: false
Expand All @@ -68,3 +67,15 @@ Lint/SpecFilename:
Lint/UselessAssign:
Excluded:
- spec/**/*

Lint/WhitespaceAroundMacroExpression:
Enabled: false

Style/PercentLiteralDelimiters:
Enabled: false

Style/RedundantSelf:
Enabled: false

Lint/SpecEqWithBoolOrNilLiteral:
Enabled: false
6 changes: 6 additions & 0 deletions OPENAPI_DOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3253,6 +3253,12 @@ paths:
schema:
type: integer
format: Int64
- name: include_linked
in: query
description: include guests from linked (child) bookings
example: "true"
schema:
type: boolean
- name: instance
in: query
description: a recurring instance id
Expand Down
61 changes: 61 additions & 0 deletions spec/controllers/bookings_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,67 @@ describe Bookings do
body.map(&.["name"]).should eq([guest.name])
end

it "#guest_list with include_linked should include guests from child bookings" do
tenant = get_tenant
user_email = Faker::Internet.email

parent = BookingsHelper.create_booking(
tenant_id: tenant.id.not_nil!,
user_email: user_email,
booking_type: "group",
)

parent_guest = GuestsHelper.create_guest(tenant.id, "parent-guest@example.com")
Attendee.create!(booking_id: parent.id.not_nil!,
guest_id: parent_guest.id,
tenant_id: parent_guest.tenant_id,
checked_in: false,
visit_expected: true,
)

child = BookingsHelper.create_booking(
tenant_id: tenant.id.not_nil!,
user_email: user_email,
booking_type: "visitor",
parent_id: parent.id,
)

child_guest = GuestsHelper.create_guest(tenant.id, "child-guest@example.com")
Attendee.create!(booking_id: child.id.not_nil!,
guest_id: child_guest.id,
tenant_id: child_guest.tenant_id,
checked_in: false,
visit_expected: true,
)

# Without include_linked: only the parent's own guest is returned
body = JSON.parse(client.get("#{BOOKINGS_BASE}/#{parent.id}/guests", headers: headers).body).as_a
body.map(&.["email"]).should eq([parent_guest.email])

# With include_linked: guests from both parent and child are returned
body = JSON.parse(client.get("#{BOOKINGS_BASE}/#{parent.id}/guests?include_linked=true", headers: headers).body).as_a
emails = body.map(&.["email"].as_s).sort!
emails.should eq(["child-guest@example.com", "parent-guest@example.com"])

# include_linked on a child booking should be silently ignored —
# only the child's own guests are returned
body = JSON.parse(client.get("#{BOOKINGS_BASE}/#{child.id}/guests?include_linked=true", headers: headers).body).as_a
body.map(&.["email"].as_s).should eq([child_guest.email])

# Deduplication: add the same guest (parent_guest) to the child booking.
# The API should return only one entry per email address.
Attendee.create!(booking_id: child.id.not_nil!,
guest_id: parent_guest.id,
tenant_id: parent_guest.tenant_id,
checked_in: false,
visit_expected: true,
)

body = JSON.parse(client.get("#{BOOKINGS_BASE}/#{parent.id}/guests?include_linked=true", headers: headers).body).as_a
emails = body.map(&.["email"].as_s).sort!
emails.should eq(["child-guest@example.com", "parent-guest@example.com"])
end

describe "permission", tags: ["auth", "group-event"] do
it "#add_attendee should NOT allow adding public or same tenant users to PRIVATE bookings" do
WebMock.stub(:post, "#{ENV["PLACE_URI"]}/auth/oauth/token")
Expand Down
24 changes: 22 additions & 2 deletions src/controllers/bookings.cr
Original file line number Diff line number Diff line change
Expand Up @@ -963,10 +963,30 @@ class Bookings < Application

# returns a list of guests associated with a booking
@[AC::Route::GET("/:id/guests")]
def guest_list : Array(Guest)
booking.attendees.to_a.map do |visitor|
def guest_list(
@[AC::Param::Info(description: "include guests from linked (child) bookings", example: "true")]
include_linked : Bool = false,
) : Array(Guest)
guests = booking.attendees.to_a.map do |visitor|
visitor.guest.not_nil!.for_booking_to_h(visitor, booking)
end

if include_linked && booking.parent?
Booking.where(parent_id: booking.id)
.join(:left, Attendee, :booking_id)
.join(:left, Guest, "guests.id = attendees.guest_id")
.to_a.each do |child|
child.attendees.to_a.each do |visitor|
guests << visitor.guest.not_nil!.for_booking_to_h(visitor, child)
end
end

# Deduplicate by email in case a guest appears on both the parent
# and a child booking
guests.uniq! { |guest| guest.email.downcase }
end

guests
end

# marks the standalone visitor as checked-in or checked-out based on the state param
Expand Down
Loading