fix: schedule-for-route spec compliance: invalid date 400, headsign fallback, route-scoped ServiceDateOutOfRange#1044
Conversation
…k, ServiceDateOutOfRange)
📝 WalkthroughWalkthroughThe PR adds a database query to detect route future service availability and refactors the ChangesSchedule Handler Service Availability & Date Validation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Performance Smoke Test ResultsStatus: PASSED
Smoke test config: 5 VUs x 30s. Thresholds: p(95) < 300ms, error rate < 1%. Full results uploaded as workflow artifact: k6-smoke-summary. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
internal/restapi/schedule_for_route_handler.go (1)
202-230: 💤 Low valueConsider deduplicating fallback stop IDs before querying.
When multiple trips lack headsigns and share the same last stop, the current implementation appends the same
stopIDmultiple times tofallbackStopIDs(line 214), then queries all of them viaGetStopsByIDs(line 218). While deduplication happens later when inserting intoseenHeadsigns(line 225), the database query could be more efficient.⚡ Optional optimization to deduplicate stop IDs
// Collect headsigns; fall back to last stop name when a trip has no headsign. seenHeadsigns := make(map[string]bool) var headsigns []string -var fallbackStopIDs []string +fallbackStopIDSet := make(map[string]struct{}) for _, trip := range tripsInGroup { hs := trip.TripHeadsign.String if hs != "" { if !seenHeadsigns[hs] { seenHeadsigns[hs] = true headsigns = append(headsigns, hs) } } else if sts := stopTimesByTrip[trip.ID]; len(sts) > 0 { - fallbackStopIDs = append(fallbackStopIDs, sts[len(sts)-1].StopID) + fallbackStopIDSet[sts[len(sts)-1].StopID] = struct{}{} } } -if len(fallbackStopIDs) > 0 { +if len(fallbackStopIDSet) > 0 { + fallbackStopIDs := make([]string, 0, len(fallbackStopIDSet)) + for sid := range fallbackStopIDSet { + fallbackStopIDs = append(fallbackStopIDs, sid) + } lastStops, err := api.GtfsManager.GtfsDB.Queries.GetStopsByIDs(ctx, fallbackStopIDs)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/restapi/schedule_for_route_handler.go` around lines 202 - 230, The loop that builds fallbackStopIDs (iterating tripsInGroup and using stopTimesByTrip) can append duplicate stop IDs, causing GetStopsByIDs to be called with repeated IDs; before calling api.GtfsManager.GtfsDB.Queries.GetStopsByIDs, deduplicate fallbackStopIDs (e.g., build a map[string]struct{} or seen map while collecting or transform fallbackStopIDs into a unique slice) and pass the unique slice to GetStopsByIDs, keeping the existing seenHeadsigns and headsigns logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/restapi/schedule_for_route_handler.go`:
- Around line 97-108: The noTripsResponse closure currently conflates DB errors
with no-future-service by treating any err from
GtfsManager.GtfsDB.Queries.RouteHasFutureService (RouteHasFutureService) as
ServiceDateOutOfRange; change it so that if err != nil the function returns a
500 server-error ResponseModel (include server error context), otherwise if
hasFuture == 0 return the 510 ServiceDateOutOfRange response, and otherwise call
buildNoServiceThatDayResponse as before; also update the call sites that invoke
noTripsResponse to handle/propagate the 500 case appropriately instead of
assuming a date-range error.
---
Nitpick comments:
In `@internal/restapi/schedule_for_route_handler.go`:
- Around line 202-230: The loop that builds fallbackStopIDs (iterating
tripsInGroup and using stopTimesByTrip) can append duplicate stop IDs, causing
GetStopsByIDs to be called with repeated IDs; before calling
api.GtfsManager.GtfsDB.Queries.GetStopsByIDs, deduplicate fallbackStopIDs (e.g.,
build a map[string]struct{} or seen map while collecting or transform
fallbackStopIDs into a unique slice) and pass the unique slice to GetStopsByIDs,
keeping the existing seenHeadsigns and headsigns logic unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ecf1b6c2-163a-4137-88cb-28246d258a77
📒 Files selected for processing (8)
gtfsdb/db.gogtfsdb/models.gogtfsdb/query.sqlgtfsdb/query.sql.gointernal/restapi/openapi_conformance_test.gointernal/restapi/responses.gointernal/restapi/schedule_for_route_handler.gointernal/restapi/schedule_for_route_handler_test.go
|
Performance Smoke Test ResultsStatus: PASSED
Smoke test config: 5 VUs x 30s. Thresholds: p(95) < 300ms, error rate < 1%. Full results uploaded as workflow artifact: k6-smoke-summary. |



Fixes: #992, #1042, #1043
Summary
Fixes three spec gaps found during review of the
schedule-for-routeendpoint.1- Invalid date (HTTP 400)
Unparseable
dateparams now return{"fieldErrors":{"date":[..]}}with HTTP 400,matching the Java OBA reference implementation. Previously returned HTTP 200 & code 510.
2- Headsign fallback to last stop name
When a trip has no
trip_headsign, the name of its last stop is used as the fallback,per the spec. Previously such trips were silently skipped.
3- Route-scoped
ServiceDateOutOfRangeReplaced the feed-wide end-date check with a
RouteHasFutureServiceSQL query thatchecks whether this specific route has calendar coverage after the requested date.
Routes that end before the feed does now correctly return
ServiceDateOutOfRangeinstead of
NoServiceThatDay.Summary by CodeRabbit
Bug Fixes
Improvements