From 8d0b8037c633f9a88258e6e3cb4473b2779360aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Mon, 29 Oct 2018 21:37:49 +0000 Subject: [PATCH] feat(project): add support for loading extra css/js files --- assets.go | 14 ++++++ assets_test.go | 82 ++++++++++++++++++++++++++++++++++ docs/CONFIGURATION.md | 28 ++++++++++++ docs/example.yaml | 3 ++ internal/config/config.go | 5 +++ internal/config/config_test.go | 7 +++ internal/config/models.go | 4 ++ main.go | 8 ++++ ui/public/index.html | 2 + 9 files changed, 153 insertions(+) create mode 100644 assets_test.go diff --git a/assets.go b/assets.go index 17fb2ecdc..4a51216c5 100644 --- a/assets.go +++ b/assets.go @@ -4,9 +4,11 @@ import ( "errors" "html/template" "net/http" + "os" "strings" assetfs "github.com/elazarl/go-bindata-assetfs" + "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" ) @@ -72,3 +74,15 @@ func loadTemplate(t *template.Template, path string) *template.Template { return t } + +func serverFileOrEmpty(path string, contentType string, c *gin.Context) { + if path == "" { + c.Data(200, contentType, nil) + return + } + if _, err := os.Stat(path); os.IsNotExist(err) { + c.Data(200, contentType, nil) + return + } + c.File(path) +} diff --git a/assets_test.go b/assets_test.go new file mode 100644 index 000000000..ccffc0550 --- /dev/null +++ b/assets_test.go @@ -0,0 +1,82 @@ +package main + +import ( + "net/http/httptest" + "testing" + + "github.com/prymitive/karma/internal/config" +) + +type customizationAssetsTest struct { + customJS string + customCSS string + path string + code int + body string + mime string +} + +func TestCustomizationAssets(t *testing.T) { + customizationAssetsTests := []customizationAssetsTest{ + { + path: "/custom.js", + code: 200, + body: "", + mime: "application/javascript", + }, + { + path: "/custom.css", + code: 200, + body: "", + mime: "text/css", + }, + { + customJS: "foo/bar/custom.js", + path: "/custom.js", + code: 200, + body: "", + mime: "application/javascript", + }, + { + customCSS: "foo/bar/custom.css", + path: "/custom.css", + code: 200, + body: "", + mime: "text/css", + }, + { + customJS: "ui/.env", + path: "/custom.js", + code: 200, + body: "NODE_PATH=src\nPUBLIC_URL=.\n", + mime: "text/plain; charset=utf-8", + }, + { + customCSS: "ui/.env", + path: "/custom.css", + code: 200, + body: "NODE_PATH=src\nPUBLIC_URL=.\n", + mime: "text/plain; charset=utf-8", + }, + } + + mockConfig() + for _, staticFileTest := range customizationAssetsTests { + config.Config.Custom.CSS = staticFileTest.customCSS + config.Config.Custom.JS = staticFileTest.customJS + r := ginTestEngine() + + req := httptest.NewRequest("GET", staticFileTest.path, nil) + resp := httptest.NewRecorder() + r.ServeHTTP(resp, req) + if resp.Code != staticFileTest.code { + t.Errorf("Invalid status code for GET %s: %d", staticFileTest.path, resp.Code) + } + if resp.Body.String() != staticFileTest.body { + t.Errorf("Invalid body for GET %s: %s, expected %s", staticFileTest.path, resp.Body.String(), staticFileTest.body) + } + if resp.Result().Header.Get("Content-Type") != staticFileTest.mime { + t.Errorf("Invalid Content-Type for GET %s: %s, expected %s", staticFileTest.path, resp.Result().Header.Get("Content-Type"), staticFileTest.mime) + } + } +} diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 9261a6540..35b72fe93 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -439,6 +439,34 @@ sentry: public: https://:@sentry.io/ ``` +## Customizing karma + +In order to keep the core code simple karma doesn't support any way of extending +provided functionality. There is however possibility to inject custom CSS & +JavaScript code, which can be used to either override built in CSS styles +or integrate with extra services, for example using error handlers other than +Sentry. + +```yaml +custom: + css: string + js: string +``` + +- `css` - path to a CSS file +- `js` - path to JavaScript file + +Example: + +```yaml +custom: + css: /theme/custom.css + js: /assets/custom.js +``` + +Use at your own risk and be aware that used CSS class names might change without +warning. This feature is provided as is without any guarantees. + ## Command line flags Config file options are mapped to command line flags, so `alertmanager:interval` diff --git a/docs/example.yaml b/docs/example.yaml index 08c1eba1d..92ed08693 100644 --- a/docs/example.yaml +++ b/docs/example.yaml @@ -18,6 +18,9 @@ annotations: hidden: - help visible: [] +custom: + css: /custom.css + js: /custom.js debug: false filters: default: diff --git a/internal/config/config.go b/internal/config/config.go index e9576056a..4e0f9923a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -41,6 +41,9 @@ func init() { pflag.String("config.file", "", "Full path to the configuration file") + pflag.String("custom.css", "", "Path to a file with custom CSS to load") + pflag.String("custom.js", "", "Path to a file with custom JavaScript to load") + pflag.Bool("debug", false, "Enable debug mode") pflag.StringSlice("filters.default", []string{}, "List of default filters") @@ -118,6 +121,8 @@ func (config *configSchema) Read() { config.Annotations.Default.Hidden = v.GetBool("annotations.default.hidden") config.Annotations.Hidden = v.GetStringSlice("annotations.hidden") config.Annotations.Visible = v.GetStringSlice("annotations.visible") + config.Custom.CSS = v.GetString("custom.css") + config.Custom.JS = v.GetString("custom.js") config.Debug = v.GetBool("debug") config.Filters.Default = v.GetStringSlice("filters.default") config.Labels.Color.Static = v.GetStringSlice("labels.color.static") diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 4db06d920..3f64b6e77 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -25,6 +25,8 @@ func resetEnv() { "ANNOTATIONS_HIDDEN", "ANNOTATIONS_VISIBLE", "CONFIG_FILE", + "CUSTOM_CSS", + "CUSTOM_JS", "DEBUG", "FILTERS_DEFAULT", "LABELS_COLOR_STATIC", @@ -68,6 +70,9 @@ annotations: hidden: [] visible: - summary +custom: + css: /custom.css + js: /custom.js debug: true filters: default: @@ -132,6 +137,8 @@ func TestReadConfig(t *testing.T) { os.Setenv("ALERTMANAGER_URI", "http://localhost") os.Setenv("ANNOTATIONS_DEFAULT_HIDDEN", "true") os.Setenv("ANNOTATIONS_VISIBLE", "summary") + os.Setenv("CUSTOM_CSS", "/custom.css") + os.Setenv("CUSTOM_JS", "/custom.js") os.Setenv("DEBUG", "true") os.Setenv("FILTERS_DEFAULT", "@state=active foo=bar") os.Setenv("LABELS_COLOR_STATIC", "a bb ccc") diff --git a/internal/config/models.go b/internal/config/models.go index 32d663af1..4984bf567 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -31,6 +31,10 @@ type configSchema struct { Hidden []string Visible []string } + Custom struct { + CSS string + JS string + } Debug bool Filters struct { Default []string diff --git a/main.go b/main.go index 2558e8b2a..3e9c980e5 100644 --- a/main.go +++ b/main.go @@ -85,6 +85,14 @@ func setupRouter(router *gin.Engine) { router.GET(getViewURL("/autocomplete.json"), autocomplete) router.GET(getViewURL("/labelNames.json"), knownLabelNames) router.GET(getViewURL("/labelValues.json"), knownLabelValues) + + router.GET(getViewURL("/custom.css"), func(c *gin.Context) { + serverFileOrEmpty(config.Config.Custom.CSS, "text/css", c) + }) + router.GET(getViewURL("/custom.js"), func(c *gin.Context) { + serverFileOrEmpty(config.Config.Custom.JS, "application/javascript", c) + }) + router.NoRoute(notFound) } diff --git a/ui/public/index.html b/ui/public/index.html index d05750f71..df4b5c695 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -10,6 +10,7 @@ --> + karma @@ -36,5 +37,6 @@ To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> +