Caller Inventory — find_available_dates_without_start_time & find_available_dates
Branch: refactor/move-bulk-precomputer-into-inv-checker
Date: 2026-07-06
Scope: Every reachable caller of the two InvCheckerHungryHubService methods that
the follow-up refactor will touch.
TL;DR
| Method | Direct callers | Transitive reach | Vendor‑V1‑specific cap needed? |
|---|---|---|---|
find_available_dates_without_start_time | 5 (4 in app/, 1 in bin/) | 1 (Vendor V1, via VendorAvailability::ReadService) | Yes — only Vendor V1 |
find_available_dates | 6 (5 in app/, 1 in bin/) | 1 (Vendor V1, via VendorAvailability::ReadService) | Yes — only Vendor V1 (the method already applies its own caps internally) |
Risk summary:
- High: 2 (Vendor V1, both methods — go through
VendorAvailability::ReadService) - Medium: 0
- Low: 9
The refactor is safe for every direct caller as long as the vendor‑V1‑specific caps
(expiry_date = min(restaurant.expiry_date, local_rps.map(&:end_date)) and
exceeds_max_seat?) stay in a thin wrapper (e.g., the existing
VendorAvailability::ReadService or a future controller‑level wrapper). The risk is
concentrated entirely in the Vendor V1 read path.
1. Callers of find_available_dates_without_start_time
1.1 app/controllers/api/v5/restaurants_controller.rb:258 — V5 (active restaurant, has packages)
- Calling class / context:
Api::V5::RestaurantsController#find_available_dates(the public V5 REST endpointGET /api/v5/restaurants/:id/find_available_dates.json). - Branch taken when:
restaurant.restaurant_packages.present?(active restaurant with at least one package). - What it does with the result: Returns the array directly as the JSON
datapayload (line 310–314). No post‑processing, no re‑mapping of keys. - Equivalent vendor‑V1 caps? No. This caller does NOT apply any equivalent of the
expiry_datecap orexceeds_max_seat?cap. The existing method already only caps byrestaurant.expiry_date(notrp.end_date), and it does not callexceeds_max_seat?. - Performance sensitivity: High. Public customer‑facing endpoint, 20‑day window, high traffic (drives the reservation funnel).
- Assumptions about output shape: Expects raw per‑date payload:
date,availability,seat_left,min_seat,max_seat,booked_seat,quantity_available. Matches the currentfind_available_dates_without_start_timeshape exactly. - Vendor‑V1 assumption map: A — no vendor‑V1‑specific assumptions. Drop‑in.
- Risk: Low.
- Test coverage:
spec/controllers/api/v5/restaurants_controller_spec.rbexists but does not exercise this branch (nofind_available_datesexamples in the grep above). Risk is amplified by the lack of direct test coverage, but the shape contract is exercised byvalidate_inventory_consistency.rbwhich compares this exact output to the API. - Notes: The V5 controller does its own
date > todayfiltering and 20‑day clamp BEFORE calling the method, so the method’s internal past‑date filter is redundant but harmless.
1.2 app/controllers/api/v5/restaurants_controller.rb:346 — V5 old_find_available_dates (widget web)
- Calling class / context:
Api::V5::RestaurantsController#old_find_available_dates(the legacy widget path, taken whenparams['ignore_end_date'] == 'true'). - Branch taken when:
params.fetch('ignore_end_date', true).to_s == 'true'. Used by the widget web (seeapp/assets/javascripts/widgets/book-table.js:105). - What it does with the result: Returns the array directly as the JSON
datapayload (line 349–353). - Equivalent vendor‑V1 caps? No. Same as 1.1.
- Performance sensitivity: High. Widget web is customer‑facing, called on the
restaurant detail page. Window is
today..today+30.days(ordays_in_advancefor restaurant id 1590). - Assumptions about output shape: Same raw per‑date payload as 1.1.
- Vendor‑V1 assumption map: A — drop‑in.
- Risk: Low.
- Test coverage: None directly. Only indirect via
validate_inventory_consistency.rb. - Notes: This is the
ignore_end_date=truepath. The 1.1 path is the default.
1.3 app/services/vendor_availability/read_service.rb:140 — Vendor V1 Read Service
- Calling class / context:
VendorAvailability::ReadService#available_dates_without_start_time. This is the Vendor V1 entry point — the one being refactored. - Branch taken when: Vendor V1
find_available_datescontroller is called WITHOUT arestaurant_package_idparam AND the restaurant has packages. - What it does with the result: Applies the vendor‑V1‑specific caps (see
read_service.rb:161—expiry_date = restaurant.expiry_date, then merges the per‑date payload). Returns the array to the controller which JSON‑encodes it. - Equivalent vendor‑V1 caps? Yes — but in the WRAPPER, not the inv_checker method.
The ReadService adds: (a)
expiry_date = restaurant.expiry_datecap (line 146), and the result is then post‑processed by the controller’s response shape. Theexceeds_max_seat?cap is NOT applied here (that’s only inavailable_dates, notavailable_dates_without_start_time). - Performance sensitivity: Very high. Vendor V1 is the integration used by all third‑party partners (Klook, GetYourGuide, etc.). This is the endpoint the entire PR #8274 perf overhaul is targeting.
- Assumptions about output shape: Expects the raw per‑date payload from
find_available_date(date, slug)(one row per date, with the standard keys). - Vendor‑V1 assumption map: C — relies on the caps being applied SOMEWHERE in the
read path. If the caps move into the generic
find_available_dates_without_start_time, every other caller (1.1, 1.2, 1.4, 1.5) will silently change behavior. If the caps are not preserved in the wrapper, Vendor V1 will silently return wrong results. - Risk: HIGH.
- Test coverage:
spec/requests/api/vendor/v1/restaurants_availability_contract_spec.rb:49— contract test for the no‑package‑id branch.spec/services/vendor_availability/read_service_spec.rb— direct unit test of the ReadService.
- Notes: This is the call site that the refactor is specifically trying to fold into the generic method. The wrapper must continue to add the caps (or they must be added to the generic method with a feature flag, which the task says is NOT the plan).
1.4 app/workers/vendors/getyourguide/notify_worker.rb:103 — GetYourGuide notify worker
- Calling class / context:
Vendors::Getyourguide::NotifyWorker#check_availability. Called fromprocessed_active_restaurants(line 96) for each active restaurant in the worker’s batch. - Branch taken when: Always (when
restaurant.restaurant_packages.present?). - What it does with the result: Iterates
availabilities(the return value), maps each to a date string, and only considers dates where the time‑slot has zero seats left (line 56–58). Effectively, it only cares aboutavailabilityanddatekeys. - Equivalent vendor‑V1 caps? No. The worker does not apply any equivalent of the
vendor‑V1 caps. It only reads
availabilityanddate. - Performance sensitivity: Medium. Background worker, runs hourly (or on demand),
processes a batch of restaurants. Window is
today..today+30.days. Not customer‑facing. - Assumptions about output shape: Only uses
date(viaavailability['date']) andavailability. Tolerant of the raw payload shape. - Vendor‑V1 assumption map: A — drop‑in.
- Risk: Low.
- Test coverage: None directly. The worker has no spec for
check_availability. - Notes: Background job, so the perf benefit of the batched warm‑up is real but not customer‑visible. Safe.
1.5 bin/inventory_system/validate_inventory_consistency.rb:186 — Ops consistency script
- Calling class / context:
InventoryConsistencyValidator#compare_restaurant_dates. Standalone Ruby script run viabundle exec rails runner. - Branch taken when: Always (when validating a restaurant with packages).
- What it does with the result: Compares the output to the V5 API output field‑by‑field
(
availability,seat_left,quantity_available,min_seat,max_seat,booked_seat). Reports any mismatch as a failed consistency check. - Equivalent vendor‑V1 caps? No. The script compares raw fields, no caps.
- Performance sensitivity: Low. Ops script, run on demand.
- Assumptions about output shape: Expects the exact raw per‑date payload, byte‑for‑byte match with the V5 API. This script is the closest thing to a behavioral contract test for this method.
- Vendor‑V1 assumption map: A — drop‑in. Actually, this is the strongest signal that the method must remain a drop‑in: if the enhanced method changes the shape, this script will fail.
- Risk: Low (for the refactor itself) but high signal value: any change to the output shape or field semantics will be caught here.
- Test coverage: N/A (this IS the test for production).
- Notes: This script compares the inv_checker output to the V5 API output. If the refactor changes the inv_checker output to match Vendor V1’s expected shape, the V5 comparison will fail. The refactor MUST keep the inv_checker method’s output byte‑identical to today’s output for the non‑vendor‑V1 path.
2. Callers of find_available_dates (with restaurant_package_id)
2.1 app/controllers/api/v5/concerns/inventories_v4.rb:30 — V4 packages path
- Calling class / context:
Api::V5::Concerns::InventoriesV4#available_dates_based_on_packages_v4. This is the V5 endpointGET /api/v5/restaurants/:id/available_dates_based_on_packages.json(minor_version = 4 only). - Branch taken when:
params[:restaurant_package_ids]is present and the request is V5 minor_version 4. - What it does with the result: Returns the array directly as the JSON
datapayload. - Equivalent vendor‑V1 caps? No. This caller does not apply any equivalent of the vendor‑V1 caps. It relies on the method itself to apply them.
- Performance sensitivity: Medium. V4 packages path, lower traffic than the main
find_available_datesendpoint. 20‑day window. - Assumptions about output shape: Expects raw per‑date payload with the standard
keys (
date,availability,seat_left,min_seat,max_seat,booked_seat,quantity_available). - Vendor‑V1 assumption map: A — no vendor‑V1‑specific assumptions. Drop‑in.
- Risk: Low.
- Test coverage: No direct spec for this concern. The endpoint is exercised by
bin/inventory_system/validate_inventory_consistency.rbwhich compares this output to the API (seecompare_package_datesat line 238). - Notes: The
create_inv_checker_instancehelper at line 14 pre‑configures the inv_checker with the specificrestaurant_package_ids, so the method receives a package‑scoped inv_checker.
2.2 app/controllers/api/v5/restaurants_controller.rb:303 — V5 (inactive restaurant, widget)
- Calling class / context:
Api::V5::RestaurantsController#find_available_dates, theelsebranch (restaurant has no packages). Used by the widget when the restaurant is inactive or has no packages. - Branch taken when:
restaurant.restaurant_packages.blank?(no packages). - What it does with the result: Concatenates with
dates_find_by_start_time(the today‑only special case) and returns the combined array as JSONdata. - Equivalent vendor‑V1 caps? No. The controller does not apply any equivalent
of the vendor‑V1 caps. However, the method itself (
find_available_datesininv_checker_hungry_hub_service.rb:1013) already applies the caps internally (expiry_date from rp.end_date at line 1022–1027, exceeds_max_seat? at line 1095). - Performance sensitivity: High. Widget web, customer‑facing, called on the restaurant detail page. 20‑day window.
- Assumptions about output shape: Expects the same raw per‑date payload as 2.1.
- Vendor‑V1 assumption map: B — the method already has its own equivalent of the caps. The enhanced method (with batched warm‑up) is a drop‑in.
- Risk: Low.
- Test coverage: No direct spec.
- Notes: This is the “no packages” branch of the V5 endpoint. The method’s
internal caps are a no‑op when there are no packages (the
has_packagescheck at line 1020 short‑circuits the rp.end_date logic).
2.3 app/controllers/admin/restaurant_availabilities_controller.rb:73 — Admin search
- Calling class / context:
Admin::RestaurantAvailabilitiesController#search. Admin UI for searching restaurants by availability. - Branch taken when:
params['pick_date']is anything other than'with_time'or'single'(i.e., a date range). - What it does with the result: Checks if any date in the range has
availability == true(line 83). Returns a simplestatus: true/falseJSON. - Equivalent vendor‑V1 caps? No. The controller only reads
availability. - Performance sensitivity: Low. Admin UI, low traffic. Date range is whatever the admin enters (unbounded, but typically short).
- Assumptions about output shape: Only uses
availabilitykey. Tolerant. - Vendor‑V1 assumption map: A — drop‑in.
- Risk: Low.
- Test coverage:
spec/controllers/admin/restaurant_availabilities_controller_spec.rb(not directly verified for this method, but the file exists). - Notes: Admin‑facing, so a behavioral change would be noticed quickly by the team.
2.4 app/services/vendor_availability/read_service.rb:55 — Vendor V1 Read Service
- Calling class / context:
VendorAvailability::ReadService#available_dates. This is the Vendor V1 entry point whenrestaurant_package_idIS provided. - Branch taken when: Vendor V1
find_available_datescontroller is called WITH arestaurant_package_idparam. - What it does with the result: Applies the vendor‑V1‑specific caps:
(a)
expiry_date = min(restaurant.expiry_date, local_rps.map(&:end_date))(line 63–68), (b)exceeds_max_seat?(adult, slug)cap (line 108), (c) re‑derives min_seat/max_seat from the package table (line 97–105). Returns the array to the controller. - Equivalent vendor‑V1 caps? Yes — but in the WRAPPER, not the inv_checker method.
The ReadService adds BOTH the
expiry_datecap (usingrp.end_date) and theexceeds_max_seat?cap. The inv_checker method ALSO applies these caps internally (seeinv_checker_hungry_hub_service.rb:1022–1095), so the ReadService’s caps are redundant for the inv_checker method’s output but the ReadService still applies them defensively. - Performance sensitivity: Very high. Vendor V1, the endpoint the entire PR #8274 perf overhaul is targeting. 20‑day window.
- Assumptions about output shape: Expects the raw per‑date payload from
find_available_date(date, slug). - Vendor‑V1 assumption map: C — relies on the caps being applied in the read path. The inv_checker method already applies them, so the ReadService’s caps are belt‑and‑suspenders. If the refactor changes the inv_checker method to NOT apply the caps, the ReadService’s own caps will catch it. This is the one method where the ReadService’s caps are a true safety net.
- Risk: HIGH (but mitigated — the ReadService already applies the caps itself).
- Test coverage:
spec/requests/api/vendor/v1/restaurants_availability_contract_spec.rb:25— contract test for the with‑package‑id branch.spec/services/vendor_availability/read_service_spec.rb:35— direct unit test.
- Notes: This is the second call site that the refactor is trying to fold into the
generic method. The wrapper must continue to apply the caps (or the inv_checker
method must continue to apply them, which it already does for
find_available_dates).
2.5 app/services/vendors_service/klook/availability_service.rb:74 — Klook integration
- Calling class / context:
VendorsService::Klook::AvailabilityService#calendar_dates_availability_data. Called by the Klook webhook/controller when Klook asks for calendar availability. - Branch taken when: Klook sends a
start_date+end_datepayload (date range). - What it does with the result: Maps each date to a Klook‑shaped hash
(
local_date,available,vacancies,capacity). Divides byper_pack_qtyif the package uses per‑pack pricing. - Equivalent vendor‑V1 caps? No. The service does not apply any equivalent of
the vendor‑V1 caps. It only reads
date,seat_left,quantity_available, andavailability. - Performance sensitivity: Medium. Klook webhook, called on availability updates. Date range is whatever Klook sends.
- Assumptions about output shape: Expects the raw per‑date payload with the standard
keys. Uses
date(as string),seat_left,quantity_available,availability. - Vendor‑V1 assumption map: A — drop‑in.
- Risk: Low.
- Test coverage:
spec/services/vendors_service/klook/availability_service_spec.rb:83— direct unit test. Stubs the inv_checker. - Notes: Klook is a third‑party integration, so a behavioral change would manifest as wrong availability data sent to Klook, which would be caught by Klook’s downstream consumers.
2.6 bin/inventory_system/validate_inventory_consistency.rb:256 — Ops consistency script
- Calling class / context:
InventoryConsistencyValidator#compare_package_dates. Standalone Ruby script. - Branch taken when: Always (when validating a restaurant with packages and
--package-idsis provided). - What it does with the result: Compares the output to the V5 API output
(
available_dates_based_on_packages.json) field‑by‑field. - Equivalent vendor‑V1 caps? No.
- Performance sensitivity: Low. Ops script.
- Assumptions about output shape: Expects byte‑for‑byte match with the V5 API.
- Vendor‑V1 assumption map: A — drop‑in. Strong signal that the method must remain a drop‑in for the non‑vendor‑V1 path.
- Risk: Low (for the refactor) but high signal value.
- Test coverage: N/A (this IS the test).
- Notes: Same as 1.5 — the script will catch any output shape change.
3. Callers of InvCheckerFactory / create_inv_checker_service (transitive reach)
These callers create an inv_checker instance but do NOT directly call
find_available_dates_without_start_time or find_available_dates. They are listed
for completeness because the refactor changes the internals of the inv_checker methods,
which could transitively affect them. None of these are at risk from the refactor
because they don’t call the two methods being changed.
3.1 Reachable from public APIs (vendor V1, V5, partner, admin)
| File | Line | What it calls on inv_checker | Risk |
|---|---|---|---|
app/controllers/api/vendor/v1/restaurants_controller.rb | 120 | available_packages (for find_available_packages) | None — different method |
app/controllers/api/vendor/v1/restaurants_controller.rb | 428 | restaurant_package_ids= (for find_available_start_times[_v4]) | None — different method |
app/controllers/api/v5/restaurants_controller.rb | 234, 323, 459, 479, 517, 838, 1110, 1167 | find_available_dates_without_start_time, find_available_dates, find_available_start_times[_v4], bookable?, recommend_date | See §1.1, §1.2, §2.2 |
app/controllers/api/v5/restaurant_packages_controller.rb | 137, 179, 214 | recommend_date, seat_lefts (TODO), other | None — different methods |
app/controllers/api/v5/concerns/inventories_v4.rb | 14 (via create_inv_checker_instance) | find_available_dates, find_available_start_times_v4 | See §2.1 |
app/controllers/api/partner/v1/reservations_controller.rb | 260 | bookable? | None — different method |
app/controllers/api/dashboard/reservations_controller.rb | 165 | bookable? | None — different method |
app/controllers/admin/restaurant_availabilities_controller.rb | 53 | bookable?, find_available_single_date, find_available_dates | See §2.3 |
app/controllers/admin/restaurants_controller.rb | 109, 110, 115, 1507 | bookable? | None — different method |
3.2 Internal (workers, presenters, services)
| File | Line | What it calls on inv_checker | Risk |
|---|---|---|---|
app/services/vendor_availability/read_service.rb | 192 | find_available_date (per‑date), exceeds_max_seat? (via send) | See §1.3, §2.4 |
app/workers/vendors/getyourguide/notify_worker.rb | 46, 121 | find_available_dates_without_start_time, find_available_start_times, get_inv_by_date | See §1.4 |
app/workers/vendors/restaurant_search_availability_check_worker.rb | (HTTP to V5 API, not direct inv_checker) | n/a — calls V5 API, which calls the inv_checker | Indirect — see §1.1 |
app/workers/inventory/generate_summary_worker.rb | 56 | unknown (not investigated in detail) | None expected |
app/workers/inventory_reporter_worker.rb | 25 | seat_lefts (TODO) | None |
app/workers/check_booking_availability_worker.rb | 11 | unknown | None expected |
app/services/reservation_service/init/hungry_hub.rb | 386, 416, 611 | unknown | None expected |
app/services/report_service/dashboard.rb | 40 | unknown | None expected |
app/services/partner_service/inventories/get_inventory_with_time_service.rb | 224 | unknown | None expected |
app/services/partner_service/reports/branch_covers_service.rb | 48 | unknown | None expected |
app/services/partner_service/reports/base_service.rb | 106 | unknown | None expected |
app/services/partner_service/reports/sold_out_service.rb | 70 | unknown | None expected |
app/services/event_driven_services/hh_search/schemas/restaurant_availability_schema.rb | 57 | find_available_start_times_v4 | None |
app/services/event_driven_services/hh_search/producers/restaurants/availability_producer.rb | 150, 181 | inventories (private, via method(:inventories)) | None |
app/presenters/admin/bookability_presenter.rb | 896 | find_available_date (per‑date, via ReadService for the new cache) | See §4 |
app/my_lib/modules/owners/reports.rb | 40 | unknown | None expected |
app/my_lib/package_booking/users/update.rb | 246 | unknown | None expected |
app/my_lib/modules/owners/reports/sold_out_report.rb | 44 | unknown | None expected |
app/my_lib/modules/owners/reports/branch_covers_report.rb | 17 | unknown | None expected |
app/my_lib/agents/base.rb | 145 | unknown | None expected |
app/models/restaurant.rb | 292, 314, 751 | create_inv_checkers, inv_checker_service_class | None |
app/mailers/staff_mailer.rb | 570 | unknown | None expected |
app/controllers/external_reservations_controller.rb | 109 | unknown | None expected |
app/controllers/dashboard/v2/inventories_controller.rb | 105 | unknown | None expected |
app/controllers/dashboard/v2/reservations_controller.rb | 383 | bookable? | None |
app/controllers/dashboard/v2/group/reservations_controller.rb | 47 | bookable? | None |
app/controllers/api/vendor/v1/google_reserve/availabilities_controller.rb | 100 | restaurant.create_inv_checkers | None |
app/controllers/api/vendor/v1/getyourguide/availabilities_controller.rb | 27 | restaurant.create_inv_checkers.first | None |
4. Admin::BookabilityPresenter — the admin diagnosis presenter
This is a special case. The presenter does NOT call find_available_dates_without_start_time
or find_available_dates directly. It calls find_available_date (per‑date, singular) and
VendorAvailability::ReadService#available_dates_without_start_time (the read service wrapper).
- File:
app/presenters/admin/bookability_presenter.rb - Relevant lines:
- Line 232:
legacy = safe_call({}) { checker.find_available_date(date, slug) }— reads the legacy per‑date cache. - Line 239:
(svc.available_dates_without_start_time(adult: party, kids: 0, start_date: date.to_s, end_date: date.to_s).find { |row| row['date'].to_s == date.to_s }) || {}— reads the new cache via the ReadService, for a single date. - Line 516:
dates.each { |d| checker.set_find_available_date(d, slug) }— writes the legacy cache (sanctioned write path). - Line 643:
checker.send(:calc_find_available_date, date, slug)— reads the fresh DB truth (private method, reached read‑only via send).
- Line 232:
- What it does: Compares fresh DB truth vs. legacy cache vs. new cache to diagnose bookability issues for the admin CS team.
- Vendor‑V1 assumption map: B — the presenter uses the ReadService wrapper, which applies the vendor‑V1 caps. The presenter does NOT need the caps to be in the generic inv_checker method.
- Risk: Low. The presenter’s contract is with the ReadService, not the inv_checker method directly.
- Test coverage:
spec/presenters/admin/bookability_parity_spec.rbandspec/presenters/admin/bookability_processing_spec.rb— extensive coverage. - Notes: This is the most heavily tested caller. The presenter’s attribution logic (line 217–313) explicitly compares three sources: fresh, legacy, new. If the refactor changes the inv_checker method’s output, the attribution will show a divergence and the admin CS team will see a false CACHE BUG diagnosis.
5. Spec coverage map
| Caller | Spec file | Coverage |
|---|---|---|
§1.1 V5 find_available_dates (line 258) | None direct | None — only via validate_inventory_consistency.rb |
§1.2 V5 old_find_available_dates (line 346) | None | None |
| §1.3 Vendor V1 ReadService (line 140) | restaurants_availability_contract_spec.rb:49 + read_service_spec.rb | Good |
| §1.4 GYG notify worker (line 103) | None | None |
| §1.5 validate_inventory_consistency (line 186) | N/A (this IS the test) | Excellent (byte‑for‑byte) |
| §2.1 V4 packages path (line 30) | None direct | None — only via validate_inventory_consistency.rb |
| §2.2 V5 no‑packages branch (line 303) | None | None |
| §2.3 Admin restaurant_availabilities (line 73) | spec/controllers/admin/restaurant_availabilities_controller_spec.rb (not directly verified) | Partial |
| §2.4 Vendor V1 ReadService (line 55) | restaurants_availability_contract_spec.rb:25 + read_service_spec.rb | Good |
| §2.5 Klook availability_service (line 74) | spec/services/vendors_service/klook/availability_service_spec.rb:83 | Good |
| §2.6 validate_inventory_consistency (line 256) | N/A (this IS the test) | Excellent (byte‑for‑byte) |
| §4 BookabilityPresenter | spec/presenters/admin/bookability_parity_spec.rb + spec/presenters/admin/bookability_processing_spec.rb | Excellent |
Callers with NO test coverage (riskiest to change):
- §1.1 V5
find_available_dates(line 258) — public customer‑facing endpoint - §1.2 V5
old_find_available_dates(line 346) — widget web - §1.4 GYG notify worker (line 103) — background worker
- §2.1 V4 packages path (line 30) — public V5 endpoint
- §2.2 V5 no‑packages branch (line 303) — widget web
These five are the riskiest because they have no direct spec AND they are all in
category A (drop‑in). If the refactor accidentally changes the output shape, these
will break in production with no test to catch it. The validate_inventory_consistency.rb
script will catch it for §1.1 and §2.1, but not for §1.2, §1.4, §2.2.
6. Vendor‑V1‑specific assumption map (summary)
find_available_dates_without_start_time
| Caller | Category | Notes |
|---|---|---|
| §1.3 Vendor V1 ReadService | C (relies on caps in wrapper) | The only caller that needs the caps |
| §1.1 V5 (active, has packages) | A (drop‑in) | Expects raw payload |
§1.2 V5 old_find_available_dates | A (drop‑in) | Expects raw payload |
| §1.4 GYG notify worker | A (drop‑in) | Only reads date and availability |
| §1.5 validate_inventory_consistency | A (drop‑in) | Compares raw fields |
find_available_dates
| Caller | Category | Notes |
|---|---|---|
| §2.4 Vendor V1 ReadService | C (relies on caps in wrapper) | The only caller that needs the caps (and the inv_checker method already applies them internally) |
| §2.1 V4 packages path | A (drop‑in) | Expects raw payload |
| §2.2 V5 (no packages) | B (method already has caps) | The inv_checker method already applies expiry_date from rp.end_date and exceeds_max_seat? |
| §2.3 Admin restaurant_availabilities | A (drop‑in) | Only reads availability |
| §2.5 Klook availability_service | A (drop‑in) | Expects raw payload |
| §2.6 validate_inventory_consistency | A (drop‑in) | Compares raw fields |
7. Risk ranking (summary)
High risk (2)
-
§1.3 —
VendorAvailability::ReadService#available_dates_without_start_time(Vendor V1, no package_id)- Relies on the vendor‑V1 caps being applied in the read path.
- If the caps are removed from the wrapper and not added to the generic method, Vendor V1 will silently return wrong availability data.
- Mitigation: The refactor must preserve the caps in a thin wrapper (the existing ReadService, or a future controller‑level wrapper).
-
§2.4 —
VendorAvailability::ReadService#available_dates(Vendor V1, with package_id)- Relies on the vendor‑V1 caps being applied in the read path.
- Mitigation already in place: The inv_checker method
(
find_available_datesatinv_checker_hungry_hub_service.rb:1013) already applies the caps internally (expiry_date from rp.end_date at line 1022–1027, exceeds_max_seat? at line 1095). The ReadService also applies them defensively (line 63–68, line 108). Even if the refactor changes the method, the ReadService is a safety net. - Risk is LOWER than §1.3 because of the double‑coverage, but still HIGH because the refactor might also touch the internal caps logic.
Medium risk (0)
None identified. Every direct caller is either A (drop‑in) or B (method already has caps).
Low risk (9)
- §1.1 V5
find_available_dates(line 258) — A - §1.2 V5
old_find_available_dates(line 346) — A - §1.4 GYG notify worker (line 103) — A
- §1.5 validate_inventory_consistency (line 186) — A
- §2.1 V4 packages path (line 30) — A
- §2.2 V5 no‑packages branch (line 303) — B
- §2.3 Admin restaurant_availabilities (line 73) — A
- §2.5 Klook availability_service (line 74) — A
- §2.6 validate_inventory_consistency (line 256) — A
- §4 BookabilityPresenter — B (uses ReadService)
8. “Needs investigation” items
-
§2.2 V5 no‑packages branch (line 303) — This caller hits
find_available_dateson a restaurant with NO packages. The method’s internal caps (expiry_date from rp.end_date, exceeds_max_seat?) are short‑circuited by thehas_packagescheck (line 1020 ininv_checker_hungry_hub_service.rb). The refactor should preserve this short‑circuit. Low risk but worth verifying the refactored method doesn’t accidentally add the caps unconditionally. -
§1.4 GYG notify worker (line 103) — No test coverage. The worker is a background job, so a behavioral change would not be immediately visible. Worth adding a spec before the refactor ships.
-
§1.1 and §1.2 V5
find_available_dates— No direct test coverage for the V5 controller. Thevalidate_inventory_consistency.rbscript is the only safety net. Worth adding a controller spec for the public V5 endpoint. -
§2.1 V4 packages path (line 30) — No direct test coverage. The concern is included in the V5 controller but has no dedicated spec. Worth adding one.
-
Inventory::InvCheckerSupplierService#find_available_datesoverride — The supplier service overridesfind_available_dates(line 36 ofinv_checker_supplier_service.rb) to normalize kids into adult. The refactor must preserve this override. The supplier service is used by restaurants with third‑party inventory (SevenRooms, Tablecheck, etc.). Low risk because the override just delegates tosuper, but worth verifying the refactored method still works with the supplier service.
9. Patterns noticed
-
All non‑vendor‑V1 callers are category A (drop‑in). Every direct caller of
find_available_dates_without_start_timeother than the ReadService expects the raw per‑date payload with no vendor‑V1‑specific caps. The refactor must preserve this. -
Vendor V1 is the ONLY caller that needs the caps. The ReadService is the single point of vendor‑V1‑specific behavior. This is good design — the caps are isolated in one wrapper.
-
find_available_datesalready has its own caps internally. The method atinv_checker_hungry_hub_service.rb:1013already appliesexpiry_datefromrp.end_dateandexceeds_max_seat?. The ReadService’s caps are redundant for this method (belt‑and‑suspenders). The refactor should NOT remove the internal caps — they’re a safety net. -
find_available_dates_without_start_timedoes NOT have its own caps. The method atinv_checker_hungry_hub_service/find_available_dates.rb:14only appliesexpiry_date = restaurant.expiry_date(notrp.end_date) and does NOT callexceeds_max_seat?. The ReadService is the ONLY place these caps are applied. This is the asymmetry that makes the refactor risky. -
validate_inventory_consistency.rbis the strongest behavioral contract. It compares the inv_checker output to the V5 API output byte‑for‑byte. Any shape change to the inv_checker methods will be caught here. This is the single most important safety net for the refactor. -
The admin BookabilityPresenter uses the ReadService, not the inv_checker method directly. This means the presenter’s contract is with the ReadService wrapper, not the inv_checker method. The refactor can change the inv_checker method’s internals (including the warm‑up) without affecting the presenter, AS LONG AS the ReadService’s output shape is preserved.
-
The Klook integration is the only third‑party vendor that calls
find_available_datesdirectly (not through the ReadService). All other third‑party integrations (GetYourGuide, Google Reserve) go through the vendor V1 endpoint. The Klook integration is also the only one with a direct unit test forfind_available_dates. -
The V5 controller has TWO call sites for
find_available_dates_without_start_time(line 258 and line 346). The second one (old_find_available_dates) is the legacy widget path. Both are category A. -
The
find_available_date(singular, per‑date) method has a wider reach. It’s called by the ReadService, the BookabilityPresenter, the inv_checker’s ownwarm_up_cache, and theset_find_available_datewrite path. The refactor should NOT change the per‑date method’s behavior — only the loop methods (find_available_datesandfind_available_dates_without_start_time). -
The ReadService’s
available_dates(with package_id) has DOUBLE coverage of the caps. Both the inv_checker method AND the ReadService apply them. The ReadService’savailable_dates_without_start_time(no package_id) has SINGLE coverage — only the ReadService applies the caps. This is why §1.3 is riskier than §2.4.