Skip to content

Commit 93316a3

Browse files
committed
Redirect CamelCase /fun/ URLs to space-separated forms
1 parent ed86256 commit 93316a3

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

internal/ui/fundetail/handler.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/url"
66
"sort"
77
"strings"
8+
"unicode"
89

910
"pkgstatsd/internal/chartdata"
1011
"pkgstatsd/internal/packages"
@@ -35,6 +36,12 @@ func (h *Handler) HandleFunDetail(w http.ResponseWriter, r *http.Request) {
3536

3637
category := fun.FindCategory(categoryName)
3738
if category == nil {
39+
if expanded := expandCamelCase(categoryName); expanded != categoryName {
40+
if fun.FindCategory(expanded) != nil {
41+
http.Redirect(w, r, "/fun/"+url.PathEscape(expanded)+"/"+preset, http.StatusMovedPermanently)
42+
return
43+
}
44+
}
3845
http.NotFound(w, r)
3946
return
4047
}
@@ -139,6 +146,17 @@ func compareURLFromDatasets(datasets []chartdata.Dataset) string {
139146
return "/packages?compare=" + strings.Join(names, ",")
140147
}
141148

149+
func expandCamelCase(s string) string {
150+
var b strings.Builder
151+
for i, r := range s {
152+
if i > 0 && unicode.IsUpper(r) {
153+
b.WriteByte(' ')
154+
}
155+
b.WriteRune(r)
156+
}
157+
return b.String()
158+
}
159+
142160
func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
143161
mux.HandleFunc("GET /fun/{category}/{preset}", h.HandleFunDetail)
144162
}

internal/ui/fundetail/handler_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,40 @@ func TestHandleHistory_CompareURLIncludesAllPackages(t *testing.T) {
198198
}
199199
}
200200

201+
func TestHandleFunDetail_CamelCaseRedirect(t *testing.T) {
202+
manifest, _ := layout.NewManifest([]byte(`{}`))
203+
handler := NewHandler(nil, manifest)
204+
205+
tests := []struct {
206+
input string
207+
location string
208+
}{
209+
{"DesktopEnvironments", "/fun/Desktop%20Environments/current"},
210+
{"WindowManagers", "/fun/Window%20Managers/history"},
211+
{"TerminalEmulators", "/fun/Terminal%20Emulators/current"},
212+
}
213+
214+
for _, tt := range tests {
215+
preset := "current"
216+
if strings.Contains(tt.location, "history") {
217+
preset = "history"
218+
}
219+
req := httptest.NewRequest(http.MethodGet, "/fun/"+tt.input+"/"+preset, nil)
220+
req.SetPathValue("category", tt.input)
221+
req.SetPathValue("preset", preset)
222+
rr := httptest.NewRecorder()
223+
224+
handler.HandleFunDetail(rr, req)
225+
226+
if rr.Code != http.StatusMovedPermanently {
227+
t.Errorf("%s: expected 301, got %d", tt.input, rr.Code)
228+
}
229+
if got := rr.Header().Get("Location"); got != tt.location {
230+
t.Errorf("%s: expected Location %q, got %q", tt.input, tt.location, got)
231+
}
232+
}
233+
}
234+
201235
func TestHandleFunDetail_InvalidCategory(t *testing.T) {
202236
manifest, _ := layout.NewManifest([]byte(`{}`))
203237
handler := NewHandler(nil, manifest)

0 commit comments

Comments
 (0)