update transitous connection

This commit is contained in:
Arnaud Delcasse 2025-09-25 07:39:57 +02:00
parent 8680f56006
commit 63fc3e7c83
2 changed files with 191 additions and 0 deletions

View File

@ -0,0 +1,105 @@
package transitous
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
"github.com/rs/zerolog/log"
)
// Client represents a Transitous API client
type Client struct {
baseURL string
httpClient *http.Client
}
// NewClient creates a new Transitous client
func NewClient(baseURL string) *Client {
return &Client{
baseURL: baseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
// PlanParams represents the parameters for route planning
type PlanParams struct {
FromPlace string
ToPlace string
Time *time.Time
}
// PlanResponse represents the response from the Transitous API
type PlanResponse struct {
Itineraries []Itinerary `json:"itineraries"`
}
// PlanWithResponse plans a route and returns the response
func (c *Client) PlanWithResponse(ctx context.Context, params *PlanParams) (*TransitousResponse, error) {
// Build URL with query parameters
u, err := url.Parse(fmt.Sprintf("%s/api/v4/plan", c.baseURL))
if err != nil {
return nil, fmt.Errorf("failed to parse base URL: %w", err)
}
query := u.Query()
query.Set("fromPlace", params.FromPlace)
query.Set("toPlace", params.ToPlace)
if params.Time != nil {
// Use ISO 8601 format with timezone like in the example
query.Set("time", params.Time.Format(time.RFC3339))
}
// Additional parameters matching the example
query.Set("withFares", "true")
query.Set("fastestDirectFactor", "1.5")
query.Set("joinInterlinedLegs", "false")
query.Set("maxMatchingDistance", "250")
query.Set("arriveBy", "true")
u.RawQuery = query.Encode()
// Create HTTP request
req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", "COOPGO-Platform/1.0")
log.Debug().
Str("url", u.String()).
Msg("Making Transitous API request")
// Execute request
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API request failed with status %d", resp.StatusCode)
}
// Parse response
var transitousResponse TransitousResponse
if err := json.NewDecoder(resp.Body).Decode(&transitousResponse); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
log.Debug().
Str("from", params.FromPlace).
Str("to", params.ToPlace).
Int("itineraries", len(transitousResponse.Itineraries)).
Msg("Transitous API response received")
return &transitousResponse, nil
}

View File

@ -0,0 +1,86 @@
package transitous
import "time"
// TransitousResponse represents the top-level response from Transitous API
type TransitousResponse struct {
RequestParameters map[string]interface{} `json:"requestParameters"`
DebugOutput map[string]interface{} `json:"debugOutput"`
From Place `json:"from"`
To Place `json:"to"`
Direct []interface{} `json:"direct"`
Itineraries []Itinerary `json:"itineraries"`
PreviousPageCursor string `json:"previousPageCursor,omitempty"`
NextPageCursor string `json:"nextPageCursor,omitempty"`
}
// Itinerary represents a complete journey from origin to destination
type Itinerary struct {
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Duration int `json:"duration"` // Duration in seconds
Legs []Leg `json:"legs"`
}
// Leg represents a single segment of a journey
type Leg struct {
Mode string `json:"mode"` // WALK, BUS, TRAIN, etc.
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Duration int `json:"duration"` // Duration in seconds
Distance float64 `json:"distance"` // Distance in meters
From Place `json:"from"`
To Place `json:"to"`
Route *Route `json:"route,omitempty"`
AgencyName string `json:"agencyName,omitempty"`
Headsign string `json:"headsign,omitempty"`
RouteShortName string `json:"routeShortName,omitempty"`
RouteColor string `json:"routeColor,omitempty"`
RouteTextColor string `json:"routeTextColor,omitempty"`
}
// Place represents a location (stop, station, or coordinate)
type Place struct {
Name string `json:"name"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
StopId string `json:"stopId,omitempty"`
StopCode string `json:"stopCode,omitempty"`
}
// Route represents a transit route
type Route struct {
AgencyName string `json:"agencyName"`
ShortName string `json:"shortName"`
LongName string `json:"longName"`
Type int `json:"type"`
Color string `json:"color"`
TextColor string `json:"textColor"`
Url string `json:"url,omitempty"`
}
// Agency represents a transit agency
type Agency struct {
Id string `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
Timezone string `json:"timezone"`
Lang string `json:"lang,omitempty"`
Phone string `json:"phone,omitempty"`
}
// Stop represents a transit stop or station
type Stop struct {
Id string `json:"id"`
Code string `json:"code,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
ZoneId string `json:"zoneId,omitempty"`
Url string `json:"url,omitempty"`
LocationType int `json:"locationType,omitempty"`
ParentStation string `json:"parentStation,omitempty"`
Timezone string `json:"timezone,omitempty"`
WheelchairBoarding int `json:"wheelchairBoarding,omitempty"`
}