Skip to content

Fix/status stops missing from ref stops when include schedule is false - #1353

Open
JohnAkindipe wants to merge 8 commits into
OneBusAway:mainfrom
JohnAkindipe:fix/status-stops-missing-from-ref-stops-when-includeSchedule-false
Open

Fix/status stops missing from ref stops when include schedule is false#1353
JohnAkindipe wants to merge 8 commits into
OneBusAway:mainfrom
JohnAkindipe:fix/status-stops-missing-from-ref-stops-when-includeSchedule-false

Conversation

@JohnAkindipe

@JohnAkindipe JohnAkindipe commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Fix dangling status stops in references.stops in trips-for-route

Closes #1338

Problem

trips-for-route builds references.stops exclusively from the stop IDs collected out of each entry's schedule. That collection runs only inside the includeSchedule branch — in both the normal entry loop and the loop that appends DUPLICATED (extra-run) entries from the real-time feed — so when a request asks for includeSchedule=false&includeStatus=true, no stop IDs are collected at all, while every entry's status still names a closestStop and nextStop. Those IDs end up having no corresponding value in references.stops

Fix

After both entry loops finish and before the reference is built, the handler now makes a single pass over the finished entries and records each non-nil status's closestStop and nextStop into the same stop-ID accumulator the schedule collection feeds.

Testing

TestTripsForRouteHandler_StatusStopsAreReferenced drives the handler with includeSchedule=false&includeStatus=true and asserts that every non-empty status.closestStop/nextStop across all returned entries resolves in references.stops, while also asserting that no schedule information is returned. It fails on the pre-fix code and passes post-fix.

Note

Previously, if GetStopsByIDs returned an error, the handler logged a warning and degraded references.Stops to an empty slice; the response then continued and returned 200 OK with references.Stops totally empty. This behavior contradicts CONTRIBUTING.md, on handling query failures. Now, if GetStopsByIDs returns an error the handler sends a 500 server error response instead.

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Trip results now include stops referenced by real-time status information, even when schedules are excluded.
    • Preserved handling for missing status data and malformed stop identifiers.
    • Improved error handling when referenced stop information cannot be generated.
  • Tests

    • Added coverage confirming that closest and next status stops appear in trip results without schedules.

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bd53b7ca-615e-44bf-94b7-586c44798633

📥 Commits

Reviewing files that changed from the base of the PR and between 27b3ff4 and 1e03273.

📒 Files selected for processing (4)
  • internal/restapi/reference_utils.go
  • internal/restapi/trips_for_location_handler.go
  • internal/restapi/trips_for_route_handler.go
  • internal/restapi/trips_for_route_handler_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The handlers now derive referenced stops from schedule and status data separately. The route handler propagates stop lookup errors. An integration test verifies that closest and next status stops resolve when schedules are excluded.

Changes

Status stop references

Layer / File(s) Summary
Separate schedule and status stop collection
internal/restapi/reference_utils.go, internal/restapi/trips_for_location_handler.go
The location handler passes schedules and statuses separately. The collector deduplicates IDs, preserves combined IDs, skips malformed status IDs, and performs batched stop queries.
Route reference construction and validation
internal/restapi/trips_for_route_handler.go, internal/restapi/trips_for_route_handler_test.go
The route handler derives references from completed result entries and returns lookup errors. The integration test verifies that status stop IDs resolve when schedules are excluded.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 1e032

This change restores status stop references when schedules are omitted and returns a server error instead of an empty reference set when stop lookup fails; no actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

Suggested reviewers: 3rabiii, ahmedhossamdev, aaronbrethorst

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the fix for missing status stops when schedules are excluded.
Linked Issues check ✅ Passed The changes collect closest and next status stops for all entries and test their resolution in references.stops.
Out of Scope Changes check ✅ Passed The helper refactor, handler updates, error handling, and integration test directly support the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ARCoder181105

Copy link
Copy Markdown
Collaborator

@JohnAkindipe Fix sonarcloud issues , follow what contributing.md says ; as i can there is duplications of code fix that

@aaronbrethorst

Copy link
Copy Markdown
Member

Code review

Found 1 issue:

  1. stopsReferencedByEntries is now shared by two handlers but still lives in trips_for_location_handler.go, and its doc comment no longer describes what it does. CONTRIBUTING.md says reference-building helpers belong in internal/restapi/reference_utils.go ("Reference building (internal/restapi/reference_utils.go) — building the Agency/Route/Situation reference blocks used in list/entry responses"), and c00a305 ("Build every stop reference through one helper") already moved the sibling stopReferences there for exactly this reason. The comment still says "those on each entry's schedule, plus the closest and next stops on its status" and "the in-bounds stop set is deliberately not included" — the function no longer takes entries, and trips-for-route has no in-bounds stop set. The signature change is also why both call sites now repeat the same 7-line entry → (schedules, statuses) split; a shared helper in reference_utils.go taking the two entry types (or a small shared accessor) would keep that in one place.

// stopsReferencedByEntries fetches the stops the response actually refers to:
// those on each entry's schedule, plus the closest and next stops on its status.
// The in-bounds stop set is deliberately not included — it is a candidate-trip
// selection detail, and stops on it that no returned trip serves have nothing in
// the response pointing at them.
func (api *RestAPI) stopsReferencedByEntries(ctx context.Context, schedules []*models.TripsSchedule, statuses []*models.TripStatus) ([]gtfsdb.Stop, map[string]string, error) {
stopIDsByBareID := make(map[string]string)

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@aaronbrethorst aaronbrethorst left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diagnosis is right and the fix works — I confirmed the new test fails
against main and passes with the change. With includeSchedule=false there
are no schedule stop IDs at all, so status.closestStop/nextStop are the only
thing references.stops has to resolve from, and building the stops block
purely from schedule IDs left them dangling. Reusing trips-for-location's
existing helper instead of writing a second one is the right instinct.

What I'd like changed before this lands:

1. The now-shared helper is still in a handler-specific file, and its doc
comment no longer describes it.

stopsReferencedByEntries is called from both trips_for_route_handler.go and
trips_for_location_handler.go, but lives in the latter. CONTRIBUTING.md puts
reference-building helpers in internal/restapi/reference_utils.go, and commit
c00a305 moved the sibling stopReferences there for exactly this reason.

The comment is the bigger problem, because it now misleads:

stopsReferencedByEntries fetches the stops the response actually refers to:
those on each entry's schedule, plus the closest and next stops on its status.
The in-bounds stop set is deliberately not included — it is a candidate-trip
selection detail...

There are no entries in the signature any more, and "the in-bounds stop set" is
a trips-for-location concept that means nothing to a trips-for-route reader.
Please move the function to reference_utils.go, rewrite the comment for the
shared contract, and rename it — it takes schedules and statuses now, so
something like stopsReferencedBySchedulesAndStatuses says what it does.

2. The parallel-slice signature pushes duplication into both call sites.
Both handlers now repeat the same seven lines splitting entries into
schedules/statuses (trips_for_route_handler.go ~505, and
trips_for_location_handler.go ~118). That's the duplication @soumajitgh
flagged, and it's a consequence of the signature choice rather than an oversight
— two parallel slices that must stay index-aligned is a shape worth avoiding on
its own. Consider having the helper take the pairs directly (a small
{Schedule, Status} struct, or a variadic of them) so neither caller has to
build two lists in lockstep.

3. Please note the error-handling change in the description.
A GetStopsByIDs failure used to log a warning and degrade to an empty stops
block; it now returns a 500. I think that's the right change — CONTRIBUTING.md
is explicit that a failed query shouldn't be collapsed into a
looks-like-success response — but it's a behavior change beyond the stated scope
and a reviewer shouldn't have to discover it from the diff.

Small one: typo in the new test's comment — "the whole of whatreferences.stops".

One logistics note: I just merged #1360, which restructures buildTripReferences
in the same handler, so you'll need to merge main in and resolve. The two
changes are complementary — #1360 seeds routes from the stop references, this
one changes where the stops themselves come from — but the resolution isn't
purely mechanical, so give it a careful read. Happy to re-review promptly.

…-includeSchedule-false

Resolve buildTripReferences restructure in trips-for-route-handler.go
which was restructured in OneBusAway#1360
Relocate stopsReferencedByEntries to reference_utils.go since it is used
by both trips_for_route_handler and trips_for_location_handler. Restructure
it to accept a slice of structs containing trip schedule and status.
Rename to stopsReferencedBySchedulesAndStatuses to reflect the change in
its contract
@sonarqubecloud

Copy link
Copy Markdown

@JohnAkindipe

Copy link
Copy Markdown
Contributor Author

Thanks — the requested changes have been addressed:

  • Renamed stopsReferencedByEntries to prestopsReferencedBySchedulesAndStatuses and moved it to internal/restapi/reference_utils.go. The doc comment has been updated to accurately describe its purpose.
  • Refactored the helper to accept a slice of {Schedule, Status} structs, avoiding the need to build and maintain index-aligned slices in lockstep.
  • Also noted the error-handling change in the PR description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

trips-for-route: status stops are missing from references.stops

3 participants