add velov

This commit is contained in:
Gabriel Augendre 2024-09-15 13:42:41 +02:00
parent 463513e78b
commit a29feca6ff
4 changed files with 120 additions and 1 deletions

21
main.go
View file

@ -28,6 +28,10 @@ type stopOutput struct {
Body Passages
}
type velovOutput struct {
Body Station
}
func addRoutes(api huma.API, glConfig GrandLyonConfig, now func() time.Time) {
huma.Register(api, huma.Operation{
OperationID: "healthcheck",
@ -42,7 +46,7 @@ func addRoutes(api huma.API, glConfig GrandLyonConfig, now func() time.Time) {
})
huma.Get(api, "/tcl/stop/{stopID}", func(ctx context.Context, input *struct {
StopID int `path:"stopID" doc:"Stop id to monitor. Can be obtained using https://data.grandlyon.com/jeux-de-donnees/points-arret-reseau-transports-commun-lyonnais/donnees"`
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"`
}) (*stopOutput, error) {
passages, err := getPassages(ctx, glConfig, now, input.StopID)
if errors.Is(err, errNoPassageFound) {
@ -55,6 +59,21 @@ func addRoutes(api huma.API, glConfig GrandLyonConfig, now func() time.Time) {
return &stopOutput{Body: *passages}, nil
})
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
})
}
func main() {

View file

@ -86,3 +86,45 @@ func TestGetStop(t *testing.T) {
})
})
}
func TestGetVelovStation(t *testing.T) {
_, api := humatest.New(t)
transport := httpmock.NewMockTransport()
client := &http.Client{
Transport: transport,
}
config := GrandLyonConfig{
Client: client,
}
transport.RegisterResponder(http.MethodGet,
"https://data.grandlyon.com/fr/datapusher/ws/rdata/jcd_jcdecaux.jcdvelov/all.json?maxfeatures=-1&start=1",
httpmock.NewBytesResponder(http.StatusOK, httpmock.File("./testdata/station_info.json").Bytes()))
//transport.RegisterResponder(http.MethodGet,
// "https://download.data.grandlyon.com/files/rdata/jcd_jcdecaux.jcdvelov/station_status.json",
// httpmock.NewBytesResponder(http.StatusOK, httpmock.File("./testdata/station_status.json").Bytes()))
addRoutes(api, config, time.Now)
t.Run("station not found", func(t *testing.T) {
resp := api.Get("/velov/station/0")
assert.Equal(t, resp.Code, http.StatusNotFound)
})
t.Run("station exists", func(t *testing.T) {
resp := api.Get("/velov/station/10039")
assert.Equal(t, resp.Code, http.StatusOK)
var station Station
err := json.Unmarshal(resp.Body.Bytes(), &station)
assert.NilError(t, err)
assert.DeepEqual(t, station, Station{
Name: "10039 - BOUVIER",
BikesAvailable: 9,
DocksAvailable: 7,
AvailabilityCode: 1,
})
})
}

1
testdata/station_info.json vendored Normal file

File diff suppressed because one or more lines are too long

57
velov.go Normal file
View file

@ -0,0 +1,57 @@
package main
import (
"context"
"errors"
"fmt"
"github.com/carlmjohnson/requests"
"net/http"
)
type Station struct {
Name string `json:"name"`
BikesAvailable int `json:"bikes_available"`
DocksAvailable int `json:"docks_available"`
AvailabilityCode int `json:"availability_code"`
}
type stationInfo struct {
Values []struct {
AvailabilityCode int `json:"availabilitycode"`
AvailableBikeStands int `json:"available_bike_stands"`
AvailableBikes int `json:"available_bikes"`
Name string `json:"name"`
Number int `json:"number"`
} `json:"values"`
}
var errStationNotFound = errors.New("station not found")
func getStation(ctx context.Context, client *http.Client, stationID int) (*Station, error) {
var info stationInfo
err := requests.URL("https://data.grandlyon.com/fr/datapusher/ws/rdata/jcd_jcdecaux.jcdvelov/all.json?maxfeatures=-1&start=1").
Client(client).
ToJSON(&info).
Fetch(ctx)
if err != nil {
return nil, fmt.Errorf("querying station info: %w", err)
}
station := Station{}
for _, sInfo := range info.Values {
if sInfo.Number == stationID {
station.Name = sInfo.Name
station.BikesAvailable = sInfo.AvailableBikes
station.DocksAvailable = sInfo.AvailableBikeStands
station.AvailabilityCode = sInfo.AvailabilityCode
break
}
}
if station.Name == "" {
return nil, errStationNotFound
}
return &station, nil
}