Skip to content

feat: implemented GET /api/where/route-details/{id} endpoint - #1222

Open
RaginiSharma01 wants to merge 11 commits into
OneBusAway:mainfrom
RaginiSharma01:feat/routeDetails
Open

feat: implemented GET /api/where/route-details/{id} endpoint#1222
RaginiSharma01 wants to merge 11 commits into
OneBusAway:mainfrom
RaginiSharma01:feat/routeDetails

Conversation

@RaginiSharma01

@RaginiSharma01 RaginiSharma01 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

#1040

PR description:

Added a new endpoint: GET /api/where/route-details/{id}. It returns the stops and route shape for a specific route, so client apps can draw maps of the route.

What I changed

  • internal/models/route_details.go — new response model with just routeId and stopGroupings
  • internal/restapi/route_details_handler.go — the handler, reusing existing helper functions
  • internal/restapi/routes.go — registered the new route
  • internal/restapi/route_details_handler_test.go — 9 tests covering the cases below
    2026/07/23 00:41:53 WARN GTFS feed has expired component=gtfs_updater expires_at=2026-01-01T05:29:59.000+05:30 days_overdue=202
    --- PASS: TestRouteDetailsHandler_Success (0.28s)
    === RUN   TestRouteDetailsHandler_NotFound
    --- PASS: TestRouteDetailsHandler_NotFound (0.00s)
    === RUN   TestRouteDetailsHandler_InvalidTime
    --- PASS: TestRouteDetailsHandler_InvalidTime (0.00s)
    === RUN   TestRouteDetailsHandler_InvalidRouteIDFormat
    --- PASS: TestRouteDetailsHandler_InvalidRouteIDFormat (0.00s)
    === RUN   TestRouteDetailsHandler_ServiceDateParam
    --- PASS: TestRouteDetailsHandler_ServiceDateParam (0.01s)
    === RUN   TestRouteDetailsHandler_NoActiveServiceForDate
    --- PASS: TestRouteDetailsHandler_NoActiveServiceForDate (0.01s)
    === RUN   TestRouteDetailsHandler_NoActiveTrips
    --- PASS: TestRouteDetailsHandler_NoActiveTrips (0.01s)
    === RUN   TestRouteDetailsHandler_RequiresValidApiKey
    --- PASS: TestRouteDetailsHandler_RequiresValidApiKey (0.00s)
    === RUN   TestRouteDetailsHandler_SetsCacheHeaders
    --- PASS: TestRouteDetailsHandler_SetsCacheHeaders (0.01s)
    PASS
    ok      maglev.onebusaway.org/internal/restapi  (cached)
    

Which this i tested the new route on postman
response.json

Summary by CodeRabbit

  • New Features
    • Added a Route Details REST endpoint at /api/where/route-details/{id} that returns stop groupings (with polylines and heuristic groupings) plus reference data, optionally including situation/alert references.
    • Supports filtering by time or serviceDate (via existing behavior).
  • Bug Fixes
    • Improved handling for malformed route IDs, invalid time, missing/invalid API keys, and dates with no active service/trips.
  • Tests
    • Added/expanded endpoint tests, including ETag/Cache-Control with 304 Not Modified, includeReferences=false, and deterministic reference ordering.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

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: b86af03f-1caa-4e2c-b933-c82fc5366a18

📥 Commits

Reviewing files that changed from the base of the PR and between c9bf74b and 03dce59.

📒 Files selected for processing (4)
  • internal/models/stops_for_route.go
  • internal/restapi/route_details_handler.go
  • internal/restapi/route_details_handler_test.go
  • internal/restapi/stops_for_route_handler.go

📝 Walkthrough

Walkthrough

Changes

Adds route-details response models and a handler, registers GET /api/where/route-details/{id}, and tests validation, date handling, missing data, authentication, references, stop groupings, and caching.

Route details API

Layer / File(s) Summary
Route details response and processing
internal/models/route_details.go, internal/models/stops_for_route.go, internal/restapi/route_details_handler.go, internal/restapi/stops_for_route_handler.go
Defines route-details response types, supports nested stop groups, validates route and date parameters, loads GTFS data, and builds route entries with stop groupings and references.
Endpoint protection and caching
internal/restapi/routes.go, internal/restapi/http_test.go
Registers the protected, cached endpoint and updates HTTP test helpers for request headers and 304 responses.
Route details handler coverage
internal/restapi/route_details_handler_test.go
Tests successful responses, validation errors, inactive service scenarios, authentication, references, stop groupings, deterministic ordering, and ETag behavior.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant routeDetailsHandler
  participant GTFSDatabase
  participant APIResponse
  Client->>routeDetailsHandler: GET route-details/{id}
  routeDetailsHandler->>GTFSDatabase: Load agency, route, services, stops, and alerts
  GTFSDatabase-->>routeDetailsHandler: Route data and active services
  routeDetailsHandler->>APIResponse: Build RouteDetailsEntry and ReferencesModel
  APIResponse-->>Client: JSON route details
Loading

Possibly related PRs

Suggested reviewers: fletcherw, ahmedhossamdev

🚥 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 and concisely describes the main change: adding the GET /api/where/route-details/{id} endpoint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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.

@RaginiSharma01 RaginiSharma01 changed the title Feat/route details feat: implemented GET /api/where/route-details/{id} endpoint Jul 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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/models/route_details.go`:
- Around line 4-12: Add constructors for AgencyAndId and RouteDetailsEntry
following the repository’s existing model-constructor patterns, accepting each
struct’s required fields and returning initialized instances. Keep the JSON
field mappings and existing struct fields unchanged.

In `@internal/restapi/route_details_handler_test.go`:
- Around line 103-110: Update the reference assertion in the test to compare
each route’s ID with testdata.Route1.ID, rather than accepting any non-empty ID.
Keep the existing found flag and final assertion so the test specifically
verifies the requested route remains referenced without active trips.
- Around line 151-155: Update the route details test around callAPIHandler to
assert Cache-Control exactly equals "public, max-age=300" rather than only
checking presence. Reissue the request using the returned ETag in If-None-Match
and assert the response status is http.StatusNotModified.

In `@internal/restapi/route_details_handler.go`:
- Line 14: Reduce cognitive complexity in RestAPI.routeDetailsHandler by
extracting its validation and date-loading logic into focused helper functions,
and moving response-reference assembly into a separate helper. Keep
routeDetailsHandler responsible only for orchestration while preserving the
existing validation, loading, and response behavior.
- Line 78: Update the processRouteStops invocation in the route details handler
to provide all arguments required by its current signature, preserving the
existing ctx, agencyID, routeID, serviceIDs, and false values and adding the
missing argument in the correct position.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fa3faab7-66b9-4f87-b288-5617bca84a06

📥 Commits

Reviewing files that changed from the base of the PR and between 5d59661 and 5403019.

📒 Files selected for processing (4)
  • internal/models/route_details.go
  • internal/restapi/route_details_handler.go
  • internal/restapi/route_details_handler_test.go
  • internal/restapi/routes.go

Comment thread internal/models/route_details.go
Comment thread internal/restapi/route_details_handler_test.go
Comment thread internal/restapi/route_details_handler_test.go Outdated
Comment thread internal/restapi/route_details_handler.go
Comment thread internal/restapi/route_details_handler.go Outdated
@ARCoder181105

Copy link
Copy Markdown
Collaborator

Cool!!

Great Work.. @RaginiSharma01

I will take a look on that

the CI is majorly failing due to openapi.yaml (i think so havent look at that just a gist)

few questions:

as i can see i think there is a lot of refactoring is to be done can you do this from ur side (dont scare of CI we will update the openapi.yaml in sdk-config it will pass most as far as i think) like address the coderabbit comment and sonarcloud issues

they are usually simple...

also is the response is matching with the prod endpoint which i mentioned in the issues desc (like is there similarity )

@RaginiSharma01

RaginiSharma01 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @ARCoder181105!, :)

  • Yes, I checked my response against the issue's response example — it matches, including routeId, stopGroupings (with stopIds and polylines nested correctly), and all four reference types (agencies, routes, situations, stops).

  • One difference — my response only includes the "direction" grouping type, not "heuristic":

  • heuristic grouping isn't implemented in Maglev's Go codebase yet

  • processRouteStops currently only builds direction groupings, which just reads the direction_id field already in GTFS trip data

  • Java's heuristic grouping works differently — it analyzes multiple trips to derive a synthetic "canonical" stop pattern, which is new algorithmic logic that doesn't exist to reuse yet

  • So I scoped this PR to direction only for now.

response (1).json

Extract date parsing and reference assembly into helpers to reduce cognitive complexity. Add constructors for AgencyAndId and RouteDetailsEntry models. Fix route details test to assert specific testdata Route ID and proper ETag cache headers.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/models/route_details.go (1)

4-26: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove undocumented exports. The new declarations do not meet the required exported-Go documentation convention.

  • internal/models/route_details.go#L4-L26: add identifier-leading doc comments for both models and both constructors.
  • internal/restapi/route_details_handler_test.go#L13-L20: rename RouteDetailsResponse to unexported routeDetailsResponse, or add a doc comment.
🤖 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/models/route_details.go` around lines 4 - 26, Document the exported
AgencyAndId, NewAgencyAndId, RouteDetailsEntry, and NewRouteDetailsEntry
declarations in internal/models/route_details.go with identifier-leading Go doc
comments. In internal/restapi/route_details_handler_test.go, rename
RouteDetailsResponse to routeDetailsResponse or add an identifier-leading doc
comment.

Source: Coding guidelines

🤖 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/route_details_handler_test.go`:
- Around line 152-172: Replace the manual httptest.NewServer setup in the route
details test with the established serveApiAndRetrieveEndpoint helper. Extend
that helper only as needed to support sending the If-None-Match request header,
while preserving the existing ETag, cache-control, and 304 response assertions.
- Around line 157-171: In the HTTP response test flow, replace the non-fatal
error assertions after http.Get and http.DefaultClient.Do with fatal checks that
stop execution before resp or resp2 are dereferenced. Preserve the existing
response assertions and cleanup for successful requests.

---

Outside diff comments:
In `@internal/models/route_details.go`:
- Around line 4-26: Document the exported AgencyAndId, NewAgencyAndId,
RouteDetailsEntry, and NewRouteDetailsEntry declarations in
internal/models/route_details.go with identifier-leading Go doc comments. In
internal/restapi/route_details_handler_test.go, rename RouteDetailsResponse to
routeDetailsResponse or add an identifier-leading doc comment.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 16f6cf4d-86d4-4939-a09e-c1f65fa1c579

📥 Commits

Reviewing files that changed from the base of the PR and between edc946e and 5044789.

📒 Files selected for processing (3)
  • internal/models/route_details.go
  • internal/restapi/route_details_handler.go
  • internal/restapi/route_details_handler_test.go

Comment thread internal/restapi/route_details_handler_test.go Outdated
Comment thread internal/restapi/route_details_handler_test.go Outdated
Extend serveApiAndRetrieveEndpoint to accept request headers and use it in caching tests instead of manual httptest.NewServer setup. Add identifier-leading Go doc comments for exported types and functions in models/route_details.go. Rename RouteDetailsResponse to routeDetailsResponse.
@ARCoder181105

Copy link
Copy Markdown
Collaborator

Hi @RaginiSharma01 !

Thanks again for the great work on this PR.

Just wanted to give you a quick status update on my end:

  • Code Quality Fixes: I've gone ahead and resolved a few of the minor issues flagged by CodeRabbit and SonarCloud, and pushed those fixes directly to the branch.
  • Heuristic Grouping: I am currently looking into the "heuristic" grouping fallback we discussed to determine whether it is strictly required for Maglev v1.
  • Next Steps: I will be testing the current implementation against a few different GTFS feeds to ensure edge cases are handled smoothly.

I'll get back to you soon with the results of the testing and any final updates. Thanks!

The legacy Java API always includes polylines in the route-details
endpoint and explicitly provides a "heuristic" stop grouping fallback.
Client applications rely on this precise structural shape.

This change enables polylines during stop processing and duplicates the
standard direction groupings, relabeling the copies as "heuristic",
achieving full 1-to-1 structural parity with the production API.
Unit tests are updated to assert the presence of both grouping types.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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/route_details_handler_test.go`:
- Around line 43-45: Extend the success test assertions for entry.StopGroupings
to verify that at least one grouping has a non-empty polylines/map-shape
payload, while preserving the existing type assertions for “heuristic” and
“direction”.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b96044fb-7f78-44b3-a552-2b444f7650b5

📥 Commits

Reviewing files that changed from the base of the PR and between 5044789 and c9bf74b.

📒 Files selected for processing (4)
  • internal/models/route_details.go
  • internal/restapi/http_test.go
  • internal/restapi/route_details_handler.go
  • internal/restapi/route_details_handler_test.go

Comment thread internal/restapi/route_details_handler_test.go
The legacy Java API includes an empty "subGroups" array in the route
details response for each stop group. This field was previously omitted
in Maglev because it was not defined in the model.

This change adds the SubGroups field to the StopGroup struct and explicitly
initializes it as an empty slice during stop group construction. This ensures
it marshals to an empty array rather than null, achieving full parity
with the production API shape.
Address final SonarCloud and CodeRabbit review feedback.

- Refactored routeDetailsHandler to extract database lookups (GetAgency and GetRoute) into discrete helper functions (getAgencyForHandler and getRouteForHandler), reducing Cognitive Complexity from 16 to 10 (well within the limit of 15).
- Expanded TestRouteDetailsHandler_Success to assert that at least one stop group within the groupings contains a non-empty polylines array, ensuring spatial payloads are correctly mapped.
@ARCoder181105

Copy link
Copy Markdown
Collaborator

Hi @RaginiSharma01

I pushed a few cleanup commits to your branch to bring us to 100% API parity.

So much work T_T

Changes added to your branch:

  1. Model Parity: Added the missing subGroups field to StopGroup and initialized it as [] to prevent it from being omitted from the JSON.
  2. Heuristic Fallback: Implemented a temporary fix to duplicate the direction groupings and relabel them as heuristic. This exactly matches the legacy Java fallback behavior (since parsing explicit canonical extensions is out of scope for Maglev V1).
  3. SonarCloud & CodeRabbit: Extracted database queries in routeDetailsHandler to reduce Cognitive Complexity, and added the requested polylines test assertion.

Final tasks to wrap up this PR:

  1. includeReferences: Please implement the includeReferences=true/false query parameter logic, as seen on our other endpoints.
  2. Array Sorting Parity: Please investigate if the legacy Java API explicitly sorts the output arrays (e.g., references.stops, references.routes). We need to ensure Maglev returns these in the exact same deterministic order, as Go map iterations are randomized.

Let me know if you need help...

@ARCoder181105

Copy link
Copy Markdown
Collaborator

API Parity Verification

I just ran the parity tests against multiple GTFS feeds comparing the Maglev local response to the legacy Java production response.

Everything looks incredibly clean! The structural schema perfectly matches Java . Any minor discrepancies in polylines or route references are purely due to expected dataset differences between the test feeds and production, not the codebase logic.

We are in great shape...

@RaginiSharma01

Copy link
Copy Markdown
Contributor Author

@ARCoder181105 , Thank you for this ;) ..

@sonarqubecloud

Copy link
Copy Markdown

@RaginiSharma01

RaginiSharma01 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @ARCoder181105 — I've made both fixes you asked for: includeReferences=false now properly empties out the references section, and references.routes/references.stops are sorted so the order stays consistent between calls. Added tests for both, all 10 tests pass.

and also tested via postman http://localhost:4000/api/where/route-details/25_151.json?key=test
response.json

2026/07/28 21:57:57 WARN GTFS feed has expired component=gtfs_updater expires_at=2026-01-01T05:29:59.000+05:30 days_overdue=208
--- PASS: TestRouteDetailsHandler_Success (0.30s)
=== RUN   TestRouteDetailsHandler_NotFound
--- PASS: TestRouteDetailsHandler_NotFound (0.00s)
=== RUN   TestRouteDetailsHandler_InvalidTime
--- PASS: TestRouteDetailsHandler_InvalidTime (0.00s)
=== RUN   TestRouteDetailsHandler_InvalidRouteIDFormat
--- PASS: TestRouteDetailsHandler_InvalidRouteIDFormat (0.00s)
=== RUN   TestRouteDetailsHandler_ServiceDateParam
--- PASS: TestRouteDetailsHandler_ServiceDateParam (0.01s)
=== RUN   TestRouteDetailsHandler_NoActiveServiceForDate
--- PASS: TestRouteDetailsHandler_NoActiveServiceForDate (0.00s)
=== RUN   TestRouteDetailsHandler_NoActiveTrips
--- PASS: TestRouteDetailsHandler_NoActiveTrips (0.00s)
=== RUN   TestRouteDetailsHandler_RequiresValidApiKey
--- PASS: TestRouteDetailsHandler_RequiresValidApiKey (0.00s)
=== RUN   TestRouteDetailsHandler_SetsCacheHeaders
--- PASS: TestRouteDetailsHandler_SetsCacheHeaders (0.01s)
=== RUN   TestRouteDetailsHandler_IncludeReferencesFalse
--- PASS: TestRouteDetailsHandler_IncludeReferencesFalse (0.01s)
=== RUN   TestRouteDetailsHandler_ReferencesOrderIsDeterministic
--- PASS: TestRouteDetailsHandler_ReferencesOrderIsDeterministic (0.02s)

@RaginiSharma01

Copy link
Copy Markdown
Contributor Author

Hey @ARCoder181105 , i just wanted to ask, that i had raised my pr with the fixes could you please review it.

@ARCoder181105

Copy link
Copy Markdown
Collaborator

Hi @RaginiSharma01
Don't worry I will review the PR

will get back as soon as possible...

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.

2 participants