2024-09-11 20:17:57 +02:00
package main
import (
"context"
"errors"
"fmt"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humago"
"github.com/danielgtaylor/huma/v2/humacli"
"net/http"
"time"
)
type Options struct {
Host string ` help:"Host to listen to" default:"127.0.0.1" `
Port int ` help:"Port to listen on" default:"8888" `
GrandLyonUsername string ` help:"Grand Lyon username" short:"u" required:"true" `
GrandLyonPassword string ` help:"Grand Lyon password" short:"p" required:"true" `
}
type statusOutput struct {
Body struct {
Status string ` json:"status" example:"ok" doc:"API status" `
}
}
type stopOutput struct {
Body Passages
}
2024-09-15 13:42:41 +02:00
type velovOutput struct {
Body Station
}
2024-09-11 20:17:57 +02:00
func addRoutes ( api huma . API , glConfig GrandLyonConfig , now func ( ) time . Time ) {
huma . Register ( api , huma . Operation {
OperationID : "healthcheck" ,
Method : http . MethodGet ,
Path : "/" ,
Summary : "Get API status" ,
Description : "Get the status of the API." ,
} , func ( ctx context . Context , input * struct { } ) ( * statusOutput , error ) {
resp := & statusOutput { }
resp . Body . Status = "ok"
return resp , nil
} )
huma . Get ( api , "/tcl/stop/{stopID}" , func ( ctx context . Context , input * struct {
2024-09-15 13:42:41 +02:00
StopID int ` path:"stopID" doc:"Stop id to monitor. Can be obtained using https://data.grandlyon.com/portail/fr/jeux-de-donnees/points-arret-reseau-transports-commun-lyonnais/donnees" `
2024-09-11 20:17:57 +02:00
} ) ( * stopOutput , error ) {
passages , err := getPassages ( ctx , glConfig , now , input . StopID )
if errors . Is ( err , errNoPassageFound ) {
return nil , huma . NewError ( http . StatusNotFound , "no passage found" )
}
if err != nil {
return nil , err
}
return & stopOutput { Body : * passages } , nil
} )
2024-09-15 13:42:41 +02:00
huma . Get ( api , "/velov/station/{stationID}" , func ( ctx context . Context , input * struct {
StationID int ` path:"stationID" doc:"Station id to monitor. Can be obtained using https://data.grandlyon.com/portail/fr/jeux-de-donnees/stations-velo-v-metropole-lyon/donnees" `
} ) ( * velovOutput , error ) {
station , err := getStation ( ctx , glConfig . Client , input . StationID )
if errors . Is ( err , errStationNotFound ) {
return nil , huma . NewError ( http . StatusNotFound , "station not found" )
}
if err != nil {
return nil , err
}
return & velovOutput { Body : * station } , nil
} )
2024-09-11 20:17:57 +02:00
}
func main ( ) {
// Create a CLI app which takes a port option.
cli := humacli . New ( func ( hooks humacli . Hooks , options * Options ) {
// Create a new router & API
router := http . NewServeMux ( )
api := humago . New ( router , huma . DefaultConfig ( "My API" , "1.0.0" ) )
server := http . Server {
Addr : fmt . Sprintf ( "%s:%d" , options . Host , options . Port ) ,
Handler : router ,
}
glConfig := GrandLyonConfig {
Username : options . GrandLyonUsername ,
Password : options . GrandLyonPassword ,
}
addRoutes ( api , glConfig , time . Now )
hooks . OnStart ( func ( ) {
fmt . Printf ( "Starting server on %s...\n" , server . Addr )
if err := server . ListenAndServe ( ) ; err != nil {
fmt . Printf ( "Error running server: %s\n" , err )
}
} )
hooks . OnStop ( func ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 5 * time . Second )
defer cancel ( )
if err := server . Shutdown ( ctx ) ; err != nil {
fmt . Printf ( "Error shutting down server: %s\n" , err )
}
} )
} )
cli . Run ( )
}