Skip to content

Commit cabb035

Browse files
committed
refactor httprouter -> net/http.ServeMux
1 parent 12fca78 commit cabb035

12 files changed

+68
-127
lines changed

councilmember.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313

1414
"cloud.google.com/go/storage"
1515
"github.com/jehiah/legislator/db"
16-
"github.com/julienschmidt/httprouter"
1716
)
1817

1918
func (l LegislationList) Statuses() []Status {
@@ -94,8 +93,9 @@ func (s Status) CSSClass() string {
9493
// Councilmember returns the list of councilmembers at /councilmembers/$name
9594
//
9695
// Redirects from /councilmembers/$district -> /councilmembers/$name
97-
func (a *App) Councilmember(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
98-
councilmember := ps.ByName("year")
96+
func (a *App) Councilmember(w http.ResponseWriter, r *http.Request) {
97+
98+
councilmember := r.PathValue("councilmember")
9999
// log.Printf("Councilmember %q", councilmember)
100100

101101
if i, _ := strconv.Atoi(councilmember); i > 0 && i <= 51 {

councilmembers.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"time"
1111

1212
"github.com/jehiah/legislator/db"
13-
"github.com/julienschmidt/httprouter"
1413
)
1514

1615
type Person struct {
@@ -196,7 +195,7 @@ func (p Person) PartyShort() string {
196195
}
197196

198197
// Councilmembers returns the list of councilmembers at /councilmembers
199-
func (a *App) Councilmembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
198+
func (a *App) Councilmembers(w http.ResponseWriter, r *http.Request) {
200199
T := Printer(r.Context())
201200
t := newTemplate(a.templateFS, "councilmembers.html")
202201

events.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
ics "github.com/arran4/golang-ical"
1717
"github.com/gosimple/slug"
1818
"github.com/jehiah/legislator/db"
19-
"github.com/julienschmidt/httprouter"
2019
)
2120

2221
type Event struct {
@@ -57,7 +56,7 @@ type EventPage struct {
5756
Events []Event
5857
}
5958

60-
func (a *App) Events(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
59+
func (a *App) Events(w http.ResponseWriter, r *http.Request) {
6160
r.ParseForm()
6261
templateName := "events.html"
6362
var err error

go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ require (
99
github.com/gorilla/handlers v1.5.2
1010
github.com/gosimple/slug v1.13.1
1111
github.com/jehiah/legislator v0.0.0-20240301042206-71fa4af9b12a
12-
github.com/julienschmidt/httprouter v1.3.0
1312
golang.org/x/text v0.14.0
1413
)
1514

go.sum

-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6
7575
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
7676
github.com/jehiah/legislator v0.0.0-20240301042206-71fa4af9b12a h1:VyPsm1ErkjgJE2qs5swaCA6vs9MexGBipLHNlfdOAwc=
7777
github.com/jehiah/legislator v0.0.0-20240301042206-71fa4af9b12a/go.mod h1:NwkvFxVHuh8mYUpnmuJEiIhy4i+Y1OrLS9J4NaF5QSU=
78-
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
79-
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
8078
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
8179
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
8280
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=

intro.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212

1313
"github.com/jehiah/legislator/db"
1414
"github.com/jehiah/legislator/legistar"
15-
"github.com/julienschmidt/httprouter"
1615
)
1716

1817
// IsValidFileNumber matches 0123-2020
@@ -32,11 +31,23 @@ func IsValidFileNumber(file string) bool {
3231
return true
3332
}
3433

34+
func (a *App) FileRedirect(w http.ResponseWriter, r *http.Request) {
35+
file := r.PathValue("file")
36+
if IsValidFileNumber(file) {
37+
a.IntroRedirect(w, r, file)
38+
return
39+
}
40+
if strings.HasSuffix(file, ".json") && IsValidFileNumber(strings.TrimSuffix(file, ".json")) {
41+
a.IntroJSON(w, r, file)
42+
return
43+
}
44+
http.Error(w, "Not Found", 404)
45+
}
46+
3547
// IntroRedirect redirects from /1234-2020 to the URL for File "Intro 1234-2020"
3648
//
3749
// Redirects are cached for the lifetime of the process but not persisted
38-
func (a *App) IntroRedirect(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
39-
file := ps.ByName("file")
50+
func (a *App) IntroRedirect(w http.ResponseWriter, r *http.Request, file string) {
4051
if !IsValidFileNumber(file) {
4152
http.Error(w, "Not Found", 404)
4253
return
@@ -82,8 +93,7 @@ func (a *App) IntroRedirect(w http.ResponseWriter, r *http.Request, ps httproute
8293
}
8394

8495
// IntroJSON returns a json to the URL for File "Intro 1234-2020"
85-
func (a *App) IntroJSON(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
86-
file := ps.ByName("file")
96+
func (a *App) IntroJSON(w http.ResponseWriter, r *http.Request, file string) {
8797
file = fmt.Sprintf("Int %s", strings.TrimSuffix(file, ".json"))
8898
ctx := r.Context()
8999

intro_nyc.go

+39-70
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"cloud.google.com/go/storage"
2323
"github.com/gorilla/handlers"
2424
"github.com/jehiah/legislator/legistar"
25-
"github.com/julienschmidt/httprouter"
2625
)
2726

2827
//go:embed templates/*
@@ -53,7 +52,7 @@ type CachedFile struct {
5352
}
5453

5554
// RobotsTXT renders /robots.txt
56-
func (a *App) RobotsTXT(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
55+
func (a *App) RobotsTXT(w http.ResponseWriter, r *http.Request) {
5756
w.Header().Set("content-type", "text/plain")
5857
a.addExpireHeaders(w, time.Hour*24*7)
5958
io.WriteString(w, "# robots welcome\n# https://github.com/jehiah/intro.nyc\n")
@@ -120,37 +119,12 @@ func (a *App) addExpireHeaders(w http.ResponseWriter, duration time.Duration) {
120119
w.Header().Add("Expires", time.Now().Add(duration).Format(http.TimeFormat))
121120
}
122121

123-
// L2 /:file/:year
124-
func (a *App) L2(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
125-
path := ps.ByName("year")
126-
file := ps.ByName("file")
127-
switch {
128-
case path == "local-law" && IsValidFileNumber(file):
129-
a.LocalLaw(w, r, file)
130-
case file == "local-laws":
131-
a.LocalLaws(w, r, ps)
132-
case file == "councilmembers":
133-
a.Councilmember(w, r, ps)
134-
case file == "static":
135-
a.staticHandler.ServeHTTP(w, r)
136-
case file == "data":
137-
a.ProxyJSON(w, r, ps)
138-
case file == "reports":
139-
ps = append(ps, httprouter.Param{Key: "report", Value: path})
140-
a.Reports(w, r, ps)
141-
default:
142-
http.Error(w, "Not Found", 404)
143-
}
144-
}
145-
146122
// ProxyJSON proxies to /data/file.json to gs://intronyc/build/$file.json
147-
//
148-
// Note: the router match pattern is `/:file/:year` so `:file` must be == "data"
149-
func (a *App) ProxyJSON(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
150-
path := ps.ByName("year")
123+
func (a *App) ProxyJSON(w http.ResponseWriter, r *http.Request) {
124+
path := r.PathValue("path")
151125

152126
if !strings.HasSuffix(path, ".json") {
153-
log.Printf("year %q", path)
127+
// log.Printf("year %q", path)
154128
http.Error(w, "Not Found", 404)
155129
return
156130
}
@@ -183,43 +157,11 @@ func (a *App) ProxyJSON(w http.ResponseWriter, r *http.Request, ps httprouter.Pa
183157
}
184158
}
185159

186-
// L1 handles /:file
187-
//
188-
// Redirects are cached for the lifetime of the process but not persisted
189-
func (a *App) L1(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
190-
191-
file := ps.ByName("file")
192-
switch file {
193-
case "robots.txt":
194-
a.RobotsTXT(w, r, ps)
195-
return
196-
case "local-laws":
197-
a.LocalLaws(w, r, ps)
198-
return
199-
case "councilmembers":
200-
a.Councilmembers(w, r, ps)
201-
return
202-
case "recent":
203-
a.RecentLegislation(w, r, ps)
204-
return
205-
case "reports":
206-
a.Reports(w, r, ps)
207-
return
208-
case "map":
209-
a.Map(w, r, ps)
210-
return
211-
case "events", "calendar", "events.ics":
212-
a.Events(w, r, ps)
213-
return
214-
}
215-
if IsValidFileNumber(file) {
216-
a.IntroRedirect(w, r, ps)
217-
}
218-
if strings.HasSuffix(file, ".json") && IsValidFileNumber(strings.TrimSuffix(file, ".json")) {
219-
a.IntroJSON(w, r, ps)
160+
func redirect(p string) http.HandlerFunc {
161+
return func(w http.ResponseWriter, r *http.Request) {
162+
u := &url.URL{Path: p, RawQuery: r.URL.RawQuery}
163+
http.Redirect(w, r, u.String(), 301)
220164
}
221-
http.Error(w, "Not Found", 404)
222-
return
223165
}
224166

225167
func main() {
@@ -255,10 +197,37 @@ func main() {
255197
panic(err)
256198
}
257199

258-
router := httprouter.New()
259-
router.GET("/", app.Search)
260-
router.GET("/:file/:year", app.L2)
261-
router.GET("/:file", app.L1)
200+
fileRouter := http.NewServeMux()
201+
fileRouter.HandleFunc("GET /{file}", app.FileRedirect)
202+
fileRouter.HandleFunc("GET /{file}/local-law", app.LocalLaw)
203+
204+
router := http.NewServeMux()
205+
206+
router.HandleFunc("GET /{$}", app.Search)
207+
router.HandleFunc("GET /robots.txt", app.RobotsTXT)
208+
router.HandleFunc("GET /recent", app.RecentLegislation)
209+
router.HandleFunc("GET /map", app.Map)
210+
router.HandleFunc("GET /calendar", app.Events)
211+
router.HandleFunc("GET /events", app.Events)
212+
router.HandleFunc("GET /events.ics", app.Events)
213+
router.HandleFunc("GET /councilmembers", app.Councilmembers)
214+
router.HandleFunc("GET /councilmembers/{councilmember}", app.Councilmember)
215+
router.HandleFunc("GET /local-laws", app.LocalLaws)
216+
router.HandleFunc("GET /local-laws/{year}", app.LocalLaws)
217+
router.HandleFunc("GET /data/{path}", app.ProxyJSON)
218+
router.HandleFunc("GET /reports/", redirect("/reports/session")) // redirect -> /reports/session
219+
router.Handle("GET /static/", app.staticHandler)
220+
221+
router.HandleFunc("GET /reports/most_sponsored", app.ReportMostSponsored)
222+
router.HandleFunc("GET /reports/status", redirect("/reports/session")) // /reports/session
223+
router.HandleFunc("GET /reports/session", app.ReportBySession)
224+
router.HandleFunc("GET /reports/similarity", app.ReportSimilarity)
225+
router.HandleFunc("GET /reports/councilmembers", app.ReportCouncilmembers)
226+
router.HandleFunc("GET /reports/committees", app.ReportCommittees)
227+
router.HandleFunc("GET /reports/attendance", app.ReportAttendance)
228+
router.HandleFunc("GET /reports/resubmit", redirect("/reports/reintroductions"))
229+
230+
router.Handle("/", fileRouter)
262231

263232
// Determine port for HTTP service.
264233
port := os.Getenv("PORT")

local_laws.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"time"
1313

1414
"github.com/jehiah/legislator/legistar"
15-
"github.com/julienschmidt/httprouter"
1615
)
1716

1817
type LocalLaw struct {
@@ -82,8 +81,10 @@ func groupLaws(l []LocalLaw) []LocalLawYear {
8281
}
8382

8483
// LocalLaws returns the list of local laws at /local-laws
85-
func (a *App) LocalLaws(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
86-
path := ps.ByName("year")
84+
// and handles /local-laws/2024
85+
// and handles /local-laws/2024-102
86+
func (a *App) LocalLaws(w http.ResponseWriter, r *http.Request) {
87+
path := r.PathValue("year")
8788
ctx := r.Context()
8889
if matched, _ := regexp.MatchString("^(19|20)[9012][0-9]-[0-9]{1,3}$", path); matched {
8990
c := strings.Split(path, "-")
@@ -202,7 +203,8 @@ func (a *App) LocalLaws(w http.ResponseWriter, r *http.Request, ps httprouter.Pa
202203
// URL: /1234-2020/local-law
203204
//
204205
// file == 1234-2020
205-
func (a *App) LocalLaw(w http.ResponseWriter, r *http.Request, file string) {
206+
func (a *App) LocalLaw(w http.ResponseWriter, r *http.Request) {
207+
file := r.PathValue("file")
206208
ctx := r.Context()
207209
if !IsValidFileNumber(file) {
208210
http.Error(w, "Not Found", 404)

map.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ import (
44
"log"
55
"net/http"
66
"time"
7-
8-
"github.com/julienschmidt/httprouter"
97
)
108

11-
func (a *App) Map(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
9+
func (a *App) Map(w http.ResponseWriter, r *http.Request) {
1210
T := Printer(r.Context())
1311
t := newTemplate(a.templateFS, "map.html")
1412
w.Header().Set("content-type", "text/html")

recent.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313

1414
"cloud.google.com/go/storage"
1515
"github.com/jehiah/legislator/db"
16-
"github.com/julienschmidt/httprouter"
1716
)
1817

1918
type RecentLegislation struct {
@@ -93,7 +92,7 @@ func NewDateGroups(r []RecentLegislation) []DateGroup {
9392
}
9493

9594
// RecentLegislation returns the list of legislation changes /recent
96-
func (a *App) RecentLegislation(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
95+
func (a *App) RecentLegislation(w http.ResponseWriter, r *http.Request) {
9796

9897
t := newTemplate(a.templateFS, "recent_legislation.html")
9998

reports.go

-30
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"cloud.google.com/go/storage"
1616
"github.com/gosimple/slug"
1717
"github.com/jehiah/legislator/db"
18-
"github.com/julienschmidt/httprouter"
1918
)
2019

2120
func TrimCommittee(s string) string {
@@ -28,35 +27,6 @@ func TrimCommittee(s string) string {
2827
return s
2928
}
3029

31-
// Reports handles /reports/...
32-
func (a *App) Reports(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
33-
switch ps.ByName("report") {
34-
case "":
35-
http.Redirect(w, r, "/reports/session", 302)
36-
case "most_sponsored":
37-
a.ReportMostSponsored(w, r)
38-
case "status":
39-
http.Redirect(w, r, "/reports/session", 301)
40-
case "session":
41-
a.ReportBySession(w, r)
42-
case "similarity":
43-
a.ReportSimilarity(w, r)
44-
case "councilmembers":
45-
a.ReportCouncilmembers(w, r)
46-
case "committees":
47-
a.ReportCommittees(w, r)
48-
case "attendance":
49-
a.ReportAttendance(w, r)
50-
case "resubmit":
51-
u := &url.URL{Path: "/reports/reintroductions", RawQuery: r.URL.RawQuery}
52-
http.Redirect(w, r, u.String(), 301)
53-
case "reintroductions":
54-
a.ReportReintroductions(w, r)
55-
default:
56-
http.Error(w, "Not Found", 404)
57-
}
58-
}
59-
6030
type CommitteeSponsorship struct {
6131
BodyName string
6232
Sponsors int

search.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ import (
44
"log"
55
"net/http"
66
"time"
7-
8-
"github.com/julienschmidt/httprouter"
97
)
108

119
// Search returns the root path of `/` for in-browser search
12-
func (a *App) Search(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
10+
func (a *App) Search(w http.ResponseWriter, r *http.Request) {
1311
T := Printer(r.Context())
1412
t := newTemplate(a.templateFS, "index.html")
1513
w.Header().Set("content-type", "text/html")

0 commit comments

Comments
 (0)