Skip to content

Fix/arrival-and-departure-for-stop endpoint#962

Draft
3rabiii wants to merge 15 commits into
OneBusAway:mainfrom
3rabiii:fix/arrival-departure-endpoint
Draft

Fix/arrival-and-departure-for-stop endpoint#962
3rabiii wants to merge 15 commits into
OneBusAway:mainfrom
3rabiii:fix/arrival-departure-endpoint

Conversation

@3rabiii
Copy link
Copy Markdown
Contributor

@3rabiii 3rabiii commented May 18, 2026

Description

This PR addresses several critical functional parity gaps in the arrival-and-departure-for-stop API endpoint to ensure it matches the legacy OneBusAway implementation and strictly follows the API specification.

Key Changes

  • Gap 1 vehicleId: Formats the vehicle ID into the combined {agencyId}_{vehicleId} structure instead of returning the raw GTFS ID.

  • Gap 2: Sparse Sequence & Arrival/Departure Fix`: Computed a 0-based stop_ordinal in SQL to handle sparse GTFS sequences. This stable ordinal dynamically controls the arrival/departureEnabled flags and prevents 404 errors by safely matching client echo-requests.

  • Gap 3 routeShortName: Implements the 3-step narrative fallback for route short names.

  • Gap 4 tripHeadsign: Implements the stop-level narrative override for trip headsigns.

  • Gap 5 lastUpdateTime: Fixes the JSON serialization tag so it correctly emits 0 when there is no real-time vehicle data, instead of omitting the field entirely.

  • Gap 6 status & Cancelled Trips: Properly handles CANCELED schedule relationships by setting the entry status to "CANCELED", zeroing out predictions, stripping the vehicle ID, and omitting the tripStatus object.

  • Gap 7 Dynamic status: Maps non-scheduled GTFS-RT statuses (like ADDED or DUPLICATED) directly to the top-level entry status instead of unconditionally returning "default".

  • Gap 8 Prediction Zeroing: Ensures predictedArrivalTime and predictedDepartureTime are reset to 0 when predicted is false, rather than erroneously falling back to the scheduled times.

  • Gap 9 serviceDate Parsing: Supports YYYY-MM-DD date strings in the serviceDate parameter and correctly localizes dates to the agency's timezone to prevent calendar-day shifts.

  • Gap 10 time Parsing: Supports yyyy-MM-dd_HH-mm-ss datetime strings in the time parameter and localizes it to the agency's timezone.

  • Gap 11 includeReferences: Supports includeReferences=false for the singular endpoint to omit reference details (Agencies, Routes, Stops, Trips) from the response when requested.

  • Gap 12 stopSequence Search: Implements the expand-outward search algorithm for stopSequence matching, permitting tolerance to shifted indices from schedule modifications.

  • Gap 13 Closest Stop Visit: Resolves loop route ambiguity by selecting the closest scheduled stop visit using SQLite-driven absolute time difference calculations when stopSequence is absent.

  • Gap 14 blockTripSequence Fix: Accurately computes the 0-based trip sequence within a block by filtering active services. Updates the sentinel value to -1 for blockless trips and ensures the first trip (index 0) is correctly sent to the client.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected vehicle ID formatting in arrival and departure responses
    • Fixed prediction time handling when unavailable
    • Improved accuracy of stop availability calculations
    • Enhanced trip cancellation state handling
  • Chores

    • Improved error handling in database tests
    • Updated time comparison assertions in tests

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This pull request standardizes database resource cleanup patterns across gtfsdb, fixes a model serialization issue in the ArrivalAndDeparture struct, updates model test assertions for field access changes, and refactors the arrival and departure response handler to compute additional response semantics including headsign overrides, vehicle identification, predicted time handling, and stop-sequence-aware enablement logic.

Changes

Arrival and Departure Response Flow Improvements

Layer / File(s) Summary
Database query resource cleanup
gtfsdb/block_layover_test.go, gtfsdb/stops_rtree.go
Database row defer-close patterns in TestBuildBlockLayoverIndex_PopulatesTable, GetActiveStopsWithinBounds, GetStopIDsWithinBounds, and GetActiveRoutesWithinBounds are standardized to use inline anonymous functions that explicitly ignore close errors rather than direct defer calls.
Model serialization fixes
internal/models/arrival_and_departure.go
ArrivalAndDeparture.LastUpdateTime JSON struct tag is corrected by removing the non-standard ,omitzero option, leaving only the plain lastUpdateTime field name.
Model test assertions
internal/models/current_time_test.go, internal/models/trip_details_test.go
Test equality checks in TestCurrentTimeModel, TestCurrentTimeData, and TestNewEmptyTripDetails are updated to call field methods (Equal() and IsZero()) directly on the time-typed fields instead of accessing nested .Time fields.
Arrival and departure response handler refactor
internal/restapi/arrival_and_departure_for_stop_handler.go
Handler response construction is refactored to use StopHeadsign.String for headsign values, combine agency ID with vehicle ID via utils.FormCombinedID, explicitly zero predicted times when unavailable, compute routeShortName and tripHeadsign overrides, derive entryStatus from trip status, calculate arrivalEnabled/departureEnabled based on stop sequence position, and implement special cancellation state handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • OneBusAway/maglev#880: Both PRs modify gtfsdb/block_layover_test.go cursor-closing defer patterns in the same test block.
  • OneBusAway/maglev#910: Both PRs refactor internal/restapi/arrival_and_departure_for_stop_handler.go response handling and parameter logic.
  • OneBusAway/maglev#893: Both PRs modify the predicted arrival/departure time path in arrival_and_departure_for_stop_handler.go.

Suggested reviewers

  • fletcherw
  • Ahmedhossamdev
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix/arrival-and-departure-for-stop endpoint' directly and clearly describes the main change—fixing the arrival-and-departure-for-stop API endpoint—which is the core focus of all modifications in this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@3rabiii 3rabiii marked this pull request as draft May 18, 2026 00:29
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/arrival_and_departure_for_stop_handler.go`:
- Around line 373-375: The code is deriving a zero-based stop ordinal from raw
GTFS stop_times.stop_sequence (stopSequenceZeroBased) which can be sparse and
causes mismatches with the lookup that uses params.StopSequence; instead compute
a trip-relative ordinal in the DB/query layer and return that same ordinal with
targetStopTime and totalStopsInTrip so request matching and response use the
same value. Update the call site that uses
GetTargetStopTimeWithTotalStopsBySequence(...) to request and receive a
trip-relative ordinal (not raw stop_sequence), stop using params.StopSequence
directly for lookup, and replace stopSequenceZeroBased, arrivalEnabled and
departureEnabled calculations to use the returned trip-relative ordinal and
totalStopsInTrip (reference symbols: GetTargetStopTimeWithTotalStopsBySequence,
params.StopSequence, targetStopTime, totalStopsInTrip, stopSequenceZeroBased,
arrivalEnabled, departureEnabled).
🪄 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: 5e6ad429-2af9-4ee3-badc-d1d38685fff4

📥 Commits

Reviewing files that changed from the base of the PR and between 2f29632 and 64f5c18.

📒 Files selected for processing (6)
  • gtfsdb/block_layover_test.go
  • gtfsdb/stops_rtree.go
  • internal/models/arrival_and_departure.go
  • internal/models/current_time_test.go
  • internal/models/trip_details_test.go
  • internal/restapi/arrival_and_departure_for_stop_handler.go

Comment on lines +373 to +375
stopSequenceZeroBased := int(targetStopTime.StopSequence) - 1
arrivalEnabled := stopSequenceZeroBased > 0
departureEnabled := stopSequenceZeroBased < totalStopsInTrip-1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don’t derive the public stop sequence from raw stop_times.stop_sequence.

This now emits a zero-based stopSequence, but the lookup path above still passes params.StopSequence straight into GetTargetStopTimeWithTotalStopsBySequence(...). That means a client echoing back the value returned here can miss the row entirely, and arrivalEnabled / departureEnabled also become wrong on trips whose GTFS stop sequences are sparse or don’t start at 1. Please compute a trip-relative ordinal in the query layer and use that same ordinal consistently for both request matching and response fields.

Also applies to: 417-417

🤖 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/arrival_and_departure_for_stop_handler.go` around lines 373
- 375, The code is deriving a zero-based stop ordinal from raw GTFS
stop_times.stop_sequence (stopSequenceZeroBased) which can be sparse and causes
mismatches with the lookup that uses params.StopSequence; instead compute a
trip-relative ordinal in the DB/query layer and return that same ordinal with
targetStopTime and totalStopsInTrip so request matching and response use the
same value. Update the call site that uses
GetTargetStopTimeWithTotalStopsBySequence(...) to request and receive a
trip-relative ordinal (not raw stop_sequence), stop using params.StopSequence
directly for lookup, and replace stopSequenceZeroBased, arrivalEnabled and
departureEnabled calculations to use the returned trip-relative ordinal and
totalStopsInTrip (reference symbols: GetTargetStopTimeWithTotalStopsBySequence,
params.StopSequence, targetStopTime, totalStopsInTrip, stopSequenceZeroBased,
arrivalEnabled, departureEnabled).

@3rabiii 3rabiii force-pushed the fix/arrival-departure-endpoint branch from 64f5c18 to b9228ce Compare May 18, 2026 17:08
@sonarqubecloud
Copy link
Copy Markdown

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.

1 participant