From cf9f2f8ae3e9c984f4f322c87aa282cf2bd75fae Mon Sep 17 00:00:00 2001 From: Alexis Couvreur Date: Mon, 31 Oct 2022 17:41:20 +0000 Subject: [PATCH] feat(strategy): add dynamic theme listing endpoint `/strategies/dynamic/themes` --- app/http/pages/render.go | 4 +- app/http/routes/strategies.go | 27 ++++++++- app/http/routes/strategies_test.go | 91 ++++++++++++++++++++++++++++++ app/http/server.go | 1 + 4 files changed, 118 insertions(+), 5 deletions(-) diff --git a/app/http/pages/render.go b/app/http/pages/render.go index 8b031d6..0bc7074 100644 --- a/app/http/pages/render.go +++ b/app/http/pages/render.go @@ -13,7 +13,7 @@ import ( ) //go:embed themes/* -var themes embed.FS +var Themes embed.FS type RenderOptionsInstanceState struct { Name string @@ -54,7 +54,7 @@ func Render(options RenderOptions, writer io.Writer) error { tpl, err = template.ParseFS(options.CustomThemes, fmt.Sprintf("%s.html", options.Theme)) } else { // Load from the embedded FS - tpl, err = template.ParseFS(themes, fmt.Sprintf("themes/%s.html", options.Theme)) + tpl, err = template.ParseFS(Themes, fmt.Sprintf("themes/%s.html", options.Theme)) } if err != nil { diff --git a/app/http/routes/strategies.go b/app/http/routes/strategies.go index 543745d..77238a3 100644 --- a/app/http/routes/strategies.go +++ b/app/http/routes/strategies.go @@ -40,7 +40,7 @@ func NewServeStrategy(sessionsManager sessions.Manager, conf config.Strategy) *S if conf.Dynamic.CustomThemesPath != "" { customThemesFs := osDirFS(conf.Dynamic.CustomThemesPath) serveStrategy.customThemesFS = customThemesFs - serveStrategy.customThemes = loadAllowedThemes(customThemesFs) + serveStrategy.customThemes = listThemes(customThemesFs) } return serveStrategy @@ -84,6 +84,26 @@ func (s *ServeStrategy) ServeDynamic(c *gin.Context) { } } +func (s *ServeStrategy) ServeDynamicThemes(c *gin.Context) { + + customThemes := []string{} + for theme := range s.customThemes { + customThemes = append(customThemes, theme) + } + sort.Strings(customThemes) + + embeddedThemes := []string{} + for theme := range listThemes(pages.Themes) { + embeddedThemes = append(embeddedThemes, strings.TrimPrefix(theme, "themes/")) + } + sort.Strings(embeddedThemes) + + c.JSON(http.StatusOK, map[string]interface{}{ + "custom": customThemes, + "embedded": embeddedThemes, + }) +} + func (s *ServeStrategy) ServeBlocking(c *gin.Context) { request := models.BlockingRequest{ Timeout: s.StrategyConfig.Blocking.DefaultTimeout, @@ -142,7 +162,8 @@ func instanceStateToRenderOptionsRequestState(instanceState *instance.State) pag } } -func loadAllowedThemes(dir fs.FS) (allowedThemes map[string]bool) { +func listThemes(dir fs.FS) (themes map[string]bool) { + themes = make(map[string]bool) fs.WalkDir(dir, ".", func(path string, d fs.DirEntry, err error) error { if err != nil { return err @@ -154,7 +175,7 @@ func loadAllowedThemes(dir fs.FS) (allowedThemes map[string]bool) { if strings.HasSuffix(d.Name(), ".html") { log.Debugf("found theme at \"%s\" can be loaded using \"%s\"", path, strings.TrimSuffix(path, ".html")) - allowedThemes[strings.TrimSuffix(path, ".html")] = true + themes[strings.TrimSuffix(path, ".html")] = true } else { log.Tracef("ignoring file \"%s\" because it has no .html suffix", path) } diff --git a/app/http/routes/strategies_test.go b/app/http/routes/strategies_test.go index 6c89112..2934a79 100644 --- a/app/http/routes/strategies_test.go +++ b/app/http/routes/strategies_test.go @@ -278,3 +278,94 @@ func TestNewServeStrategy(t *testing.T) { }) } } + +func TestServeStrategy_ServeDynamicThemes(t *testing.T) { + type fields struct { + StrategyConfig config.Strategy + } + tests := []struct { + name string + fields fields + osDirFS fs.FS + expected map[string]any + }{ + { + name: "load custom themes", + fields: fields{StrategyConfig: config.Strategy{ + Dynamic: config.DynamicStrategy{ + CustomThemesPath: "my/path/to/themes", + }, + }}, + osDirFS: fstest.MapFS{ + "my/path/to/superman.html": {Data: []byte("superman")}, + "my/path/to/themes/marvel.html": {Data: []byte("thor")}, + "my/path/to/themes/dc-comics.html": {Data: []byte("batman")}, + "my/path/to/themes/inner/dc-comics.html": {Data: []byte("batman")}, + }, + expected: map[string]any{ + "custom": []any{ + "dc-comics", + "inner/dc-comics", + "marvel", + }, + "embedded": []any{ + "ghost", + "hacker-terminal", + "matrix", + "shuffle", + }, + }, + }, + { + name: "load without custom themes", + expected: map[string]any{ + "custom": []any{}, + "embedded": []any{ + "ghost", + "hacker-terminal", + "matrix", + "shuffle", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + oldosDirFS := osDirFS + defer func() { osDirFS = oldosDirFS }() + + myOsDirFS := func(dir string) fs.FS { + fs, err := fs.Sub(tt.osDirFS, dir) + + if err != nil { + panic(err) + } + + return fs + } + + osDirFS = myOsDirFS + + s := NewServeStrategy(nil, tt.fields.StrategyConfig) + + recorder := httptest.NewRecorder() + c := GetTestGinContext(recorder) + + s.ServeDynamicThemes(c) + + res := recorder.Result() + defer res.Body.Close() + + jsonRes := make(map[string]interface{}) + err := json.NewDecoder(res.Body).Decode(&jsonRes) + + if err != nil { + panic(err) + } + + assert.DeepEqual(t, jsonRes, tt.expected) + + }) + } +} diff --git a/app/http/server.go b/app/http/server.go index 269a220..79a3b82 100644 --- a/app/http/server.go +++ b/app/http/server.go @@ -23,6 +23,7 @@ func Start(serverConf config.Server, strategyConf config.Strategy, sessionManage { strategy := routes.NewServeStrategy(sessionManager, strategyConf) api.GET("/strategies/dynamic", strategy.ServeDynamic) + api.GET("/strategies/dynamic/themes", strategy.ServeDynamicThemes) api.GET("/strategies/blocking", strategy.ServeBlocking) } }