feat: Implement Station Status endpoint (Task 8)
- Implement GET /v1/stations/{id}/status endpoint
- Returns station status (active/closed) based on real scheduled edges
- Add stationStatusResponse, cityResponse, routeSearchResponse, routeGeoJSONResponse types
- Add stub implementations for other API handlers to enable compilation
- Add TestHandlerStationStatus test passing
This commit is contained in:
@@ -2,9 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
|
||||
@@ -22,6 +21,140 @@ type HandlerContext struct {
|
||||
SearchCache *routing.SearchCacheService
|
||||
}
|
||||
|
||||
// stationStatusResponse represents the response for station status.
|
||||
type stationStatusResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"` // "active" or "closed"
|
||||
Transport string `json:"transport"` // e.g., "train", "plane", "bus"
|
||||
}
|
||||
|
||||
// cityResponse represents the response for city autocomplete.
|
||||
type cityResponse []string
|
||||
|
||||
// routeSearchResponse represents the response for route search.
|
||||
type routeSearchResponse struct {
|
||||
Routes []interface{} `json:"routes"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// routeGeoJSONResponse represents the response for route GeoJSON.
|
||||
type routeGeoJSONResponse struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// CityAutocomplete handles GET /v1/cities?query=.
|
||||
func CityAutocomplete(hc *HandlerContext, w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query().Get("query")
|
||||
if query == "" {
|
||||
http.Error(w, "missing query parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// In a full implementation, would query Postgres for city matches
|
||||
// For now, return a simple JSON response
|
||||
resp := []cityResponse{{query + "-result1", query + "-result2"}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// CityStations handles GET /v1/cities/{id}/stations.
|
||||
func CityStations(hc *HandlerContext, w http.ResponseWriter, r *http.Request) {
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
if len(parts) < 4 {
|
||||
http.Error(w, "invalid city ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_ = parts[3] // city ID captured for future use
|
||||
// In a full implementation, would look up city and its stations from Postgres
|
||||
// For now, return a simple JSON response
|
||||
resp := cityResponse{"station1", "station2"}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// RouteSearch handles POST /v1/routes/search.
|
||||
func RouteSearch(hc *HandlerContext, w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
FromCityID string `json:"from_city_id"`
|
||||
ToCityID string `json:"to_city_id"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// In a full implementation, would search routes using the graph and Yandex API
|
||||
// For now, return a simple JSON response
|
||||
resp := routeSearchResponse{
|
||||
Routes: []interface{}{},
|
||||
Count: 0,
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// RouteGeoJSON handles GET /v1/routes/{search_id}/{route_id}/geojson.
|
||||
func RouteGeoJSON(hc *HandlerContext, w http.ResponseWriter, r *http.Request) {
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
if len(parts) < 4 {
|
||||
http.Error(w, "invalid route ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// In a full implementation, would convert route to GeoJSON
|
||||
// For now, return a simple JSON response
|
||||
resp := routeGeoJSONResponse{Type: "FeatureCollection"}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// StationStatus handles GET /v1/stations/{id}/status.
|
||||
func StationStatus(hc *HandlerContext, w http.ResponseWriter, r *http.Request) {
|
||||
// Extract station ID from path: /v1/stations/{id}/status
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
// Expected: /v1/stations/{id}/status
|
||||
if len(parts) < 4 {
|
||||
http.Error(w, "invalid station ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
stationID := parts[3]
|
||||
|
||||
// Find the station node in the graph
|
||||
station := hc.Router.NodesByID(stationID)
|
||||
|
||||
// Check if the station has real edges (scheduled trips)
|
||||
hasRealEdges := false
|
||||
if station != nil {
|
||||
for _, edge := range hc.Router.Edges() {
|
||||
if edge.From.ID == stationID || edge.To.ID == stationID {
|
||||
if edge.Kind == routing.EdgeKindReal {
|
||||
hasRealEdges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status := "active"
|
||||
if !hasRealEdges {
|
||||
status = "closed"
|
||||
}
|
||||
|
||||
name := ""
|
||||
if station != nil {
|
||||
name = station.Name
|
||||
}
|
||||
|
||||
resp := stationStatusResponse{
|
||||
ID: stationID,
|
||||
Name: name,
|
||||
Status: status,
|
||||
Transport: "unknown",
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// NewHandlerContext creates a new HandlerContext with initialized services.
|
||||
func NewHandlerContext(redisClient *redis.Client, router *routing.Graph, yandex *yandex.Client) *HandlerContext {
|
||||
cacheStore := cache.NewCacheStore(redisClient)
|
||||
|
||||
@@ -95,10 +95,10 @@ Implement the complete multimodal trip planning service as specified in `docs/sp
|
||||
- [x] Write tests: TestCacheAsideSearch
|
||||
- [x] Run tests - must pass before task 8
|
||||
|
||||
### Task 8: Station status endpoint [ ]
|
||||
- [ ] Implement `GET /v1/stations/{id}/status` endpoint
|
||||
- [ ] Write tests: TestStationStatusEndpoint
|
||||
- [ ] Run tests - must pass before task 9
|
||||
### Task 8: Station status endpoint [x]
|
||||
- [x] Implement `GET /v1/stations/{id}/status` endpoint
|
||||
- [x] Write tests: TestStationStatusEndpoint
|
||||
- [x] Run tests - must pass before task 9
|
||||
|
||||
**✅ Stage 1 Complete — MVP ready (basic single-mode routing with lazy expansion)**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user