mirror of
https://github.com/Crocmagnon/lyon-transports.git
synced 2024-11-21 13:38:06 +01:00
add velov
This commit is contained in:
parent
463513e78b
commit
a29feca6ff
4 changed files with 120 additions and 1 deletions
21
main.go
21
main.go
|
@ -28,6 +28,10 @@ type stopOutput struct {
|
||||||
Body Passages
|
Body Passages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type velovOutput struct {
|
||||||
|
Body Station
|
||||||
|
}
|
||||||
|
|
||||||
func addRoutes(api huma.API, glConfig GrandLyonConfig, now func() time.Time) {
|
func addRoutes(api huma.API, glConfig GrandLyonConfig, now func() time.Time) {
|
||||||
huma.Register(api, huma.Operation{
|
huma.Register(api, huma.Operation{
|
||||||
OperationID: "healthcheck",
|
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 {
|
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) {
|
}) (*stopOutput, error) {
|
||||||
passages, err := getPassages(ctx, glConfig, now, input.StopID)
|
passages, err := getPassages(ctx, glConfig, now, input.StopID)
|
||||||
if errors.Is(err, errNoPassageFound) {
|
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
|
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() {
|
func main() {
|
||||||
|
|
42
main_test.go
42
main_test.go
|
@ -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
1
testdata/station_info.json
vendored
Normal file
File diff suppressed because one or more lines are too long
57
velov.go
Normal file
57
velov.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue