diff --git a/pkg/api/api.go b/pkg/api/api.go index d4d23b17ef4..4024655775e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -209,7 +209,7 @@ func (hs *HttpServer) registerRoutes() { r.Get("/plugins", wrap(GetPluginList)) r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById)) - r.Get("/plugins/:pluginId/readme", wrap(GetPluginReadme)) + r.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown)) r.Group("/plugins", func() { r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards)) diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index fe7122f8557..042c03f9832 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -147,15 +147,16 @@ func GetPluginDashboards(c *middleware.Context) Response { } } -func GetPluginReadme(c *middleware.Context) Response { +func GetPluginMarkdown(c *middleware.Context) Response { pluginId := c.Params(":pluginId") + name := c.Params(":name") - if content, err := plugins.GetPluginReadme(pluginId); err != nil { + if content, err := plugins.GetPluginMarkdown(pluginId, name); err != nil { if notfound, ok := err.(plugins.PluginNotFoundError); ok { return ApiError(404, notfound.Error(), nil) } - return ApiError(500, "Could not get readme", err) + return ApiError(500, "Could not get markdown file", err) } else { return Respond(200, content) } diff --git a/pkg/plugins/datasource_plugin.go b/pkg/plugins/datasource_plugin.go index 8cb0748936b..e46c2709649 100644 --- a/pkg/plugins/datasource_plugin.go +++ b/pkg/plugins/datasource_plugin.go @@ -1,15 +1,24 @@ package plugins -import "encoding/json" +import ( + "encoding/json" + "os" + "path/filepath" +) type DataSourcePlugin struct { FrontendPluginBase - Annotations bool `json:"annotations"` - Metrics bool `json:"metrics"` - Alerting bool `json:"alerting"` - BuiltIn bool `json:"builtIn"` - Mixed bool `json:"mixed"` - Routes []*AppPluginRoute `json:"routes"` + Annotations bool `json:"annotations"` + Metrics bool `json:"metrics"` + Alerting bool `json:"alerting"` + MinInterval bool `json:"minInterval,omitempty"` + CacheTimeout bool `json:"cacheTimeout,omitempty"` + MaxDataPoints bool `json:"maxDataPoints,omitempty"` + BuiltIn bool `json:"builtIn,omitempty"` + Mixed bool `json:"mixed,omitempty"` + HasHelp bool `json:"hasHelp,omitempty"` + + Routes []*AppPluginRoute `json:"-"` } func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error { @@ -21,6 +30,15 @@ func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error { return err } + // look for help markdown + helpPath := filepath.Join(p.PluginDir, "HELP.md") + if _, err := os.Stat(helpPath); os.IsNotExist(err) { + helpPath = filepath.Join(p.PluginDir, "help.md") + } + if _, err := os.Stat(helpPath); err == nil { + p.HasHelp = true + } + DataSources[p.Id] = p return nil } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index fd04852f2f4..541b37c8a8a 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -38,8 +38,8 @@ type PluginBase struct { Includes []*PluginInclude `json:"includes"` Module string `json:"module"` BaseUrl string `json:"baseUrl"` - HideFromList bool `json:"hideFromList"` - State string `json:"state"` + HideFromList bool `json:"hideFromList,omitempty"` + State string `json:"state,omitempty"` IncludedInAppId string `json:"-"` PluginDir string `json:"-"` @@ -48,9 +48,6 @@ type PluginBase struct { GrafanaNetVersion string `json:"-"` GrafanaNetHasUpdate bool `json:"-"` - - // cache for readme file contents - Readme []byte `json:"-"` } func (pb *PluginBase) registerPlugin(pluginDir string) error { diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index b6c3639cbbf..885bd5c9e03 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -3,6 +3,7 @@ package plugins import ( "encoding/json" "errors" + "fmt" "io/ioutil" "os" "path" @@ -166,30 +167,24 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { return loader.Load(jsonParser, currentDir) } -func GetPluginReadme(pluginId string) ([]byte, error) { +func GetPluginMarkdown(pluginId string, name string) ([]byte, error) { plug, exists := Plugins[pluginId] if !exists { return nil, PluginNotFoundError{pluginId} } - if plug.Readme != nil { - return plug.Readme, nil + path := filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToUpper(name))) + if _, err := os.Stat(path); os.IsNotExist(err) { + path = filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToLower(name))) } - readmePath := filepath.Join(plug.PluginDir, "README.md") - if _, err := os.Stat(readmePath); os.IsNotExist(err) { - readmePath = filepath.Join(plug.PluginDir, "readme.md") + if _, err := os.Stat(path); os.IsNotExist(err) { + return make([]byte, 0), nil } - if _, err := os.Stat(readmePath); os.IsNotExist(err) { - plug.Readme = make([]byte, 0) - return plug.Readme, nil - } - - if readmeBytes, err := ioutil.ReadFile(readmePath); err != nil { + if data, err := ioutil.ReadFile(path); err != nil { return nil, err } else { - plug.Readme = readmeBytes - return plug.Readme, nil + return data, nil } } diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts index 82fa3be99e7..25957ac35ae 100644 --- a/public/app/features/panel/metrics_tab.ts +++ b/public/app/features/panel/metrics_tab.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import {DashboardModel} from '../dashboard/model'; +import Remarkable from 'remarkable'; export class MetricsTabCtrl { dsName: string; @@ -14,9 +15,16 @@ export class MetricsTabCtrl { panelDsValue: any; addQueryDropdown: any; queryTroubleshooterOpen: boolean; + helpOpen: boolean; + hasHelp: boolean; + helpHtml: string; + hasMinInterval: boolean; + hasCacheTimeout: boolean; + hasMaxDataPoints: boolean; + animateStart: boolean; /** @ngInject */ - constructor($scope, private uiSegmentSrv, private datasourceSrv) { + constructor($scope, private $sce, private datasourceSrv, private backendSrv, private $timeout) { this.panelCtrl = $scope.ctrl; $scope.ctrl = this; @@ -34,6 +42,14 @@ export class MetricsTabCtrl { this.addQueryDropdown = {text: 'Add Query', value: null, fake: true}; // update next ref id this.panelCtrl.nextRefId = this.dashboard.getNextQueryLetter(this.panel); + this.updateDatasourceOptions(); + } + + updateDatasourceOptions() { + this.hasHelp = this.current.meta.hasHelp; + this.hasMinInterval = this.current.meta.minInterval === true; + this.hasCacheTimeout = this.current.meta.cacheTimeout === true; + this.hasMaxDataPoints = this.current.meta.maxDataPoints === true; } getOptions(includeBuiltin) { @@ -51,6 +67,7 @@ export class MetricsTabCtrl { this.current = option.datasource; this.panelCtrl.setDatasource(option.datasource); + this.updateDatasourceOptions(); } addMixedQuery(option) { @@ -67,6 +84,19 @@ export class MetricsTabCtrl { this.panelCtrl.addQuery({isNew: true}); } + toggleHelp() { + this.animateStart = false; + this.helpOpen = !this.helpOpen; + this.backendSrv.get(`/api/plugins/${this.current.meta.id}/markdown/help`).then(res => { + var md = new Remarkable(); + this.helpHtml = this.$sce.trustAsHtml(md.render(res)); + + this.$timeout(() => { + this.animateStart = true; + }, 1); + }); + } + toggleQueryTroubleshooter() { this.queryTroubleshooterOpen = !this.queryTroubleshooterOpen; } diff --git a/public/app/features/panel/partials/metrics_tab.html b/public/app/features/panel/partials/metrics_tab.html index 8ceee90e7ff..53033d0e241 100644 --- a/public/app/features/panel/partials/metrics_tab.html +++ b/public/app/features/panel/partials/metrics_tab.html @@ -11,41 +11,90 @@ on-change="ctrl.datasourceChanged($option)"> -
1m
if your data is written every minute. Access auto interval via variable $__interval
for time range
string and $__interval_ms
for numeric variable that can be used in math expressions.