From aff4790d22728d89e7e2dac8af262c92087b5b39 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Sat, 11 Sep 2021 12:27:59 +0200 Subject: Piece evry last week changes together in a big rewrite of the webui --- internal/webui/html/root.html | 17 +++------ internal/webui/html/specificStop.html | 16 +++++++++ internal/webui/html/stop.html | 11 ++++++ internal/webui/root.go | 21 ++++------- internal/webui/root_test.go | 13 ++----- internal/webui/specificStop.go | 68 +++++++++++++++++++++++++++++++++++ internal/webui/specificStop_test.go | 62 ++++++++++++++++++++++++++++++++ internal/webui/stop.go | 50 ++++++++++++++++++++++++++ internal/webui/stop_test.go | 62 ++++++++++++++++++++++++++++++++ internal/webui/utils.go | 8 +++++ internal/webui/webui.go | 2 ++ 11 files changed, 293 insertions(+), 37 deletions(-) create mode 100644 internal/webui/html/specificStop.html create mode 100644 internal/webui/html/stop.html create mode 100644 internal/webui/specificStop.go create mode 100644 internal/webui/specificStop_test.go create mode 100644 internal/webui/stop.go create mode 100644 internal/webui/stop_test.go (limited to 'internal') diff --git a/internal/webui/html/root.html b/internal/webui/html/root.html index 97441c8..6b7bd6e 100644 --- a/internal/webui/html/root.html +++ b/internal/webui/html/root.html @@ -1,16 +1,9 @@ -{{ define "title"}}Horaires des prochains trains à Crépieux la Pape{{ end }} +{{ define "title"}}Trains!{{ end }} {{ template "base" . }} {{ define "main" }} -

Horaires des prochains trains à Crépieux la Pape

- - - - - - {{ range .Departures }} - - {{ end }} - -
Arrivée en gareDirection
{{ .Arrival }}{{ .DisplayName }}
+

Menu

+ {{ end }} diff --git a/internal/webui/html/specificStop.html b/internal/webui/html/specificStop.html new file mode 100644 index 0000000..5916927 --- /dev/null +++ b/internal/webui/html/specificStop.html @@ -0,0 +1,16 @@ +{{ define "title"}}Horaires des prochains trains à {{ .Stop }}{{ end }} +{{ template "base" . }} + +{{ define "main" }} +

Horaires des prochains trains à {{ .Stop }}

+ + + + + + {{ range $i, $elt := .Departures }} + + {{ end }} + +
Arrivée en gareDirection
{{ .Arrival }}{{ .Direction }}
+{{ end }} diff --git a/internal/webui/html/stop.html b/internal/webui/html/stop.html new file mode 100644 index 0000000..33a94c6 --- /dev/null +++ b/internal/webui/html/stop.html @@ -0,0 +1,11 @@ +{{ define "title"}}Choisir une gare{{ end }} +{{ template "base" . }} + +{{ define "main" }} +

Choisir une gare

+ +{{ end }} diff --git a/internal/webui/root.go b/internal/webui/root.go index 94f2169..423ba93 100644 --- a/internal/webui/root.go +++ b/internal/webui/root.go @@ -3,18 +3,16 @@ package webui import ( "fmt" "html/template" - "log" "net/http" "git.adyxax.org/adyxax/trains/pkg/model" ) -var rootTemplate = template.Must(template.ParseFS(templatesFS, "html/base.html", "html/root.html")) +var rootTemplate = template.Must(template.New("root").Funcs(funcMap).ParseFS(templatesFS, "html/base.html", "html/root.html")) // The page template variable -type Page struct { - User *model.User - Departures []model.Departure +type RootPage struct { + User *model.User } // The root handler of the webui @@ -25,16 +23,9 @@ func rootHandler(e *env, w http.ResponseWriter, r *http.Request) error { http.Redirect(w, r, "/login", http.StatusFound) return nil } - var departures []model.Departure - if departures, err := e.navitia.GetDepartures(e.conf.TrainStop); err != nil { - log.Printf("%s; data returned: %+v\n", err, departures) - return newStatusError(http.StatusInternalServerError, fmt.Errorf("Could not get departures")) - } else { - w.Header().Set("Cache-Control", "no-store, no-cache") - } - p := Page{ - User: user, - Departures: departures, + w.Header().Set("Cache-Control", "no-store, no-cache") + p := RootPage{ + User: user, } err = rootTemplate.ExecuteTemplate(w, "root.html", p) if err != nil { diff --git a/internal/webui/root_test.go b/internal/webui/root_test.go index 7e4ec71..e96f1c8 100644 --- a/internal/webui/root_test.go +++ b/internal/webui/root_test.go @@ -24,15 +24,8 @@ func TestRootHandler(t *testing.T) { require.Nil(t, err) e := env{ dbEnv: dbEnv, - conf: &config.Config{TrainStop: "test"}, + conf: &config.Config{}, } - departures1 := []model.Departure{ - model.Departure{ - Direction: "test direction", - Arrival: "20210503T150405", - }, - } - e.navitia = &NavitiaMockClient{departures: departures1, err: nil} // test GET requests runHttpTest(t, &e, rootHandler, &httpTestCase{ name: "a simple get when not logged in should redirect to the login page", @@ -46,7 +39,7 @@ func TestRootHandler(t *testing.T) { }, }) runHttpTest(t, &e, rootHandler, &httpTestCase{ - name: "a simple get when logged in should display the departure times", + name: "a simple get when logged in should display the menu", input: httpTestInput{ method: http.MethodGet, path: "/", @@ -54,7 +47,7 @@ func TestRootHandler(t *testing.T) { }, expect: httpTestExpect{ code: http.StatusOK, - bodyString: "Horaires des prochains trains", + bodyString: "Menu", }, }) } diff --git a/internal/webui/specificStop.go b/internal/webui/specificStop.go new file mode 100644 index 0000000..64dd04a --- /dev/null +++ b/internal/webui/specificStop.go @@ -0,0 +1,68 @@ +package webui + +import ( + "fmt" + "html/template" + "log" + "net/http" + "path" + "regexp" + + "git.adyxax.org/adyxax/trains/pkg/model" +) + +var validStopId = regexp.MustCompile(`^stop_area:[a-zA-Z]+:\d+$`) + +var specificStopTemplate = template.Must(template.New("specificStop").Funcs(funcMap).ParseFS(templatesFS, "html/base.html", "html/specificStop.html")) + +// The page template variable +type SpecificStopPage struct { + User *model.User + Stop string + Departures []model.Departure +} + +// The stop handler of the webui +func specificStopHandler(e *env, w http.ResponseWriter, r *http.Request) error { + if path.Dir(r.URL.Path) == "/stop" { + user, err := tryAndResumeSession(e, r) + if err != nil { + http.Redirect(w, r, "/login", http.StatusFound) + return nil + } + switch r.Method { + case http.MethodGet: + id := path.Base(r.URL.Path) + if id == "" { + return newStatusError(http.StatusBadRequest, fmt.Errorf("No id in query string")) // TODO should we redirect to root page to chose a stop id? + } + if ok := validStopId.MatchString(id); !ok { + return newStatusError(http.StatusBadRequest, fmt.Errorf("Invalid stop id")) + } + stop, err := e.dbEnv.GetStop(id) + if err != nil { + return newStatusError(http.StatusBadRequest, fmt.Errorf("Stop id not found in database")) // TODO do better + } + if departures, err := e.navitia.GetDepartures(stop.Id); err != nil { + log.Printf("%s; data returned: %+v\n", err, departures) + return newStatusError(http.StatusInternalServerError, fmt.Errorf("Could not get departures")) + } else { + w.Header().Set("Cache-Control", "no-store, no-cache") + p := SpecificStopPage{ + User: user, + Stop: stop.Name, + Departures: departures, + } + err = specificStopTemplate.ExecuteTemplate(w, "specificStop.html", p) + if err != nil { + return newStatusError(http.StatusInternalServerError, err) + } + return nil + } + default: + return newStatusError(http.StatusMethodNotAllowed, fmt.Errorf(http.StatusText(http.StatusMethodNotAllowed))) + } + } else { + return newStatusError(http.StatusNotFound, fmt.Errorf("Invalid path in specificStopHandler")) + } +} diff --git a/internal/webui/specificStop_test.go b/internal/webui/specificStop_test.go new file mode 100644 index 0000000..45b2c2f --- /dev/null +++ b/internal/webui/specificStop_test.go @@ -0,0 +1,62 @@ +package webui + +import ( + "net/http" + "testing" + + "git.adyxax.org/adyxax/trains/pkg/config" + "git.adyxax.org/adyxax/trains/pkg/database" + "git.adyxax.org/adyxax/trains/pkg/model" + "github.com/stretchr/testify/require" +) + +func TestSpecificStopHandler(t *testing.T) { + // test environment setup + dbEnv, err := database.InitDB("sqlite3", "file::memory:?_foreign_keys=on") + require.Nil(t, err) + err = dbEnv.Migrate() + require.Nil(t, err) + user1, err := dbEnv.CreateUser(&model.UserRegistration{Username: "user1", Password: "password1", Email: "julien@adyxax.org"}) + require.Nil(t, err) + _, err = dbEnv.Login(&model.UserLogin{Username: "user1", Password: "password1"}) + require.Nil(t, err) + token1, err := dbEnv.CreateSession(user1) + require.Nil(t, err) + err = dbEnv.ReplaceAndImportStops([]model.Stop{model.Stop{Id: "stop_area:test:01", Name: "test"}}) + require.Nil(t, err) + e := env{ + dbEnv: dbEnv, + conf: &config.Config{}, + } + departures1 := []model.Departure{ + model.Departure{ + Direction: "test direction", + Arrival: "20210503T150405", + }, + } + e.navitia = &NavitiaMockClient{departures: departures1, err: nil} + // test GET requests + runHttpTest(t, &e, specificStopHandler, &httpTestCase{ + name: "a simple get when not logged in should redirect to the login page", + input: httpTestInput{ + method: http.MethodGet, + path: "/stop/stop_area:test:01", + }, + expect: httpTestExpect{ + code: http.StatusFound, + location: "/login", + }, + }) + runHttpTest(t, &e, specificStopHandler, &httpTestCase{ + name: "a get of a subpath when logged in should display the departure times", + input: httpTestInput{ + method: http.MethodGet, + path: "/stop/stop_area:test:01", + cookie: &http.Cookie{Name: sessionCookieName, Value: *token1}, + }, + expect: httpTestExpect{ + code: http.StatusOK, + bodyString: "Horaires des prochains trains à test", + }, + }) +} diff --git a/internal/webui/stop.go b/internal/webui/stop.go new file mode 100644 index 0000000..68d592e --- /dev/null +++ b/internal/webui/stop.go @@ -0,0 +1,50 @@ +package webui + +import ( + "fmt" + "html/template" + "net/http" + + "git.adyxax.org/adyxax/trains/pkg/model" +) + +var stopTemplate = template.Must(template.New("stop").Funcs(funcMap).ParseFS(templatesFS, "html/base.html", "html/stop.html")) + +// The page template variable +type StopPage struct { + User *model.User + Stops []model.Stop +} + +// The stop handler of the webui +func stopHandler(e *env, w http.ResponseWriter, r *http.Request) error { + if r.URL.Path == "/stop" { + user, err := tryAndResumeSession(e, r) + if err != nil { + http.Redirect(w, r, "/login", http.StatusFound) + return nil + } + switch r.Method { + case http.MethodGet: + stops, err := e.dbEnv.GetStops() + if err != nil { + return newStatusError(http.StatusInternalServerError, fmt.Errorf("Could not get train stops")) + } else { + w.Header().Set("Cache-Control", "no-store, no-cache") + } + p := StopPage{ + User: user, + Stops: stops, + } + err = stopTemplate.ExecuteTemplate(w, "stop.html", p) + if err != nil { + return newStatusError(http.StatusInternalServerError, err) + } + return nil + default: + return newStatusError(http.StatusMethodNotAllowed, fmt.Errorf(http.StatusText(http.StatusMethodNotAllowed))) + } + } else { + return newStatusError(http.StatusNotFound, fmt.Errorf("Invalid path in stopHandler")) + } +} diff --git a/internal/webui/stop_test.go b/internal/webui/stop_test.go new file mode 100644 index 0000000..30e7593 --- /dev/null +++ b/internal/webui/stop_test.go @@ -0,0 +1,62 @@ +package webui + +import ( + "net/http" + "testing" + + "git.adyxax.org/adyxax/trains/pkg/config" + "git.adyxax.org/adyxax/trains/pkg/database" + "git.adyxax.org/adyxax/trains/pkg/model" + "github.com/stretchr/testify/require" +) + +func TestStopHandler(t *testing.T) { + // test environment setup + dbEnv, err := database.InitDB("sqlite3", "file::memory:?_foreign_keys=on") + require.Nil(t, err) + err = dbEnv.Migrate() + require.Nil(t, err) + user1, err := dbEnv.CreateUser(&model.UserRegistration{Username: "user1", Password: "password1", Email: "julien@adyxax.org"}) + require.Nil(t, err) + _, err = dbEnv.Login(&model.UserLogin{Username: "user1", Password: "password1"}) + require.Nil(t, err) + token1, err := dbEnv.CreateSession(user1) + require.Nil(t, err) + err = dbEnv.ReplaceAndImportStops([]model.Stop{model.Stop{Id: "stop_area:test:01", Name: "test"}}) + require.Nil(t, err) + e := env{ + dbEnv: dbEnv, + conf: &config.Config{}, + } + departures1 := []model.Departure{ + model.Departure{ + Direction: "test direction", + Arrival: "20210503T150405", + }, + } + e.navitia = &NavitiaMockClient{departures: departures1, err: nil} + // test GET requests + runHttpTest(t, &e, stopHandler, &httpTestCase{ + name: "a simple get when not logged in should redirect to the login page", + input: httpTestInput{ + method: http.MethodGet, + path: "/stop", + }, + expect: httpTestExpect{ + code: http.StatusFound, + location: "/login", + }, + }) + runHttpTest(t, &e, stopHandler, &httpTestCase{ + name: "a simple get when logged in should display the stops list", + input: httpTestInput{ + method: http.MethodGet, + path: "/stop", + cookie: &http.Cookie{Name: sessionCookieName, Value: *token1}, + }, + expect: httpTestExpect{ + code: http.StatusOK, + bodyString: "stop_area:test:01", + }, + }) +} diff --git a/internal/webui/utils.go b/internal/webui/utils.go index be8baf3..28a7add 100644 --- a/internal/webui/utils.go +++ b/internal/webui/utils.go @@ -2,6 +2,7 @@ package webui import ( "embed" + "html/template" "log" "net/http" @@ -16,6 +17,13 @@ var templatesFS embed.FS //go:embed static/* var staticFS embed.FS +// Template functions +var funcMap = template.FuncMap{ + "odd": func(i int) bool { + return i%2 == 1 + }, +} + // the environment that will be passed to our handlers type env struct { conf *config.Config diff --git a/internal/webui/webui.go b/internal/webui/webui.go index b18bc5c..63173c6 100644 --- a/internal/webui/webui.go +++ b/internal/webui/webui.go @@ -18,6 +18,8 @@ func Run(c *config.Config, dbEnv *database.DBEnv) { http.Handle("/", handler{&e, rootHandler}) http.Handle("/login", handler{&e, loginHandler}) http.Handle("/static/", http.FileServer(http.FS(staticFS))) + http.Handle("/stop", handler{&e, stopHandler}) + http.Handle("/stop/", handler{&e, specificStopHandler}) if i, err := dbEnv.CountStops(); err == nil && i == 0 { log.Printf("No trains stops data found, updating...") -- cgit v1.2.3