Plugins: Bump SystemJS to 6.14.2 (#70068)
* chore(plugins): bump systemjs to latest version * refactor(plugins): switch runtime over to use latest systemjs, add typings * refactor(plugins): use latest systemjs APIs for runtime dependency resolution * refactor(plugins): return valid urls from backend for latest systemjs import to work * revert(plugins): remove cfg from assetpath in favour of relative paths * fix(plugins): useDefault for systemjs deps to solve undefined errors * feat(plugins): add basic support for loading plugins via CDN * fix(plugins): load nested plugins with latest systemjs * feat(plugins): add back ability to transform plugin src for cdns * feat(plugins): get caching for module.js working, clean up * refactor(plugin_loader): create buildImportMap fn and more clean up * refactor(angularapp): use buildImportMap for dependencies and clean up * test(plugin_loader): fix failing test due to systemjs update * test(jest): mock systemjs amd extra in tests to prevent it breaking tests * chore(plugins): remove systemjs module-types extra, already included with system * fix(plugins): update key for invalidating cached plugins * fix: move systemjs amd define to another global so monaco can load * refactor(plugins): clean up cache buster and tests * chore(plugins): remove debug * refactor(plugins): move systemjs define to keep global cleaner * fix(plugins): set useDefault so system modules lodash references dont fail * feat(plugins): hook systemjs onload so stylesheets are applied to the dom * refactor(plugins): wrap amd formatted plugins in iife to prevent define collision * feat(plugins): support system module format for legacy plugins * test(plugincachebuster): update tests to match latest implementation * test(plugins-loader): fix up tests post module property change * fix test * Update pkg/plugins/manager/loader/assetpath/assetpath.go Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> * chore(plugin_loader): remove stray import from merge conflict * Revert "Update pkg/plugins/manager/loader/assetpath/assetpath.go" This reverts commit 0df57d1cf20f49c22c93369001c70aae46a97c42. * fix(plugin_loader): set use default for shared plugin dependencies * refactor(plugins): use leading slash for Module and BaseUrl * fix(plugins): fix resolve appending extension to cache query param * refactor(plugins): align baseurl and module paths * refactor(plugins): update builtInPlugins keys to match naming convention * refactor(plugins): minor loader clean up, fix up types * test(plugins): fix failing tests * refactor(plugins): rename cache buster systemjs plugin to cache * refactor(plugins): separate plugin_loader into smaller files * chore(plugins): clean up plugin_loader types * chore(plugin_sandbox): fix typescript error * chore(npm): remove unplug debug and pin systemjs to 6.14.1 * refactor(plugins-cdn): update loaders to use absolute module url from backend * fix(plugins): escape period in systemjs module regex * chore(plugins): delete redundant systemjs plugins * refactor(plugin_loader): move hooks into own file, add types * test(plugins): add tests for systemjs loader hooks * chore(plugins): rename systemjshooks file * chore(plugins): remove redundant systemjs cdn backend code * fix(plugins): handle loading with config.appSubUrl * chore(plugins): delete redundant plugin-cdn angular code * test(plugins): fix failing systemjs test missing pluginsCDNBaseUrl * refactor(plugins): backend provides base and module properties with AppSubUrl * fix(plugins): consider AppSubUrl for plugin logos * fix(plugins): use isHostedOnCDN util when checking for cdn hosted plugins * add new appSubURL field to config * refactor relative URL func * fix path for core app * refactor asset path input * fix(plugins): catch errors in loadPluginCss * feat(plugins-cdn): selectively transform sourceMapURL * re-add deleted test case * chore(plugins): bump to latest systemjs@6.14.2 * feat(plugins): add systemjs-cjs-extra for loading commonjs plugins --------- Co-authored-by: Will Browne <will.browne@grafana.com> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Browne
Will Browne
parent
b2f7476bb4
commit
62821c69b3
@@ -39,7 +39,8 @@ type Cfg struct {
|
||||
|
||||
GrafanaComURL string
|
||||
|
||||
GrafanaAppURL string
|
||||
GrafanaAppURL string
|
||||
GrafanaAppSubURL string
|
||||
|
||||
Features plugins.FeatureToggles
|
||||
|
||||
@@ -48,7 +49,7 @@ type Cfg struct {
|
||||
|
||||
func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string,
|
||||
awsAllowedAuthProviders []string, awsAssumeRoleEnabled bool, awsExternalId string, azure *azsettings.AzureSettings, secureSocksDSProxy setting.SecureSocksDSProxySettings,
|
||||
grafanaVersion string, logDatasourceRequests bool, pluginsCDNURLTemplate string, appURL string, tracing Tracing, features plugins.FeatureToggles, angularSupportEnabled bool,
|
||||
grafanaVersion string, logDatasourceRequests bool, pluginsCDNURLTemplate string, appURL string, appSubURL string, tracing Tracing, features plugins.FeatureToggles, angularSupportEnabled bool,
|
||||
grafanaComURL string) *Cfg {
|
||||
return &Cfg{
|
||||
log: log.New("plugin.cfg"),
|
||||
@@ -67,6 +68,7 @@ func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSetti
|
||||
Tracing: tracing,
|
||||
GrafanaComURL: grafanaComURL,
|
||||
GrafanaAppURL: appURL,
|
||||
GrafanaAppSubURL: appSubURL,
|
||||
Features: features,
|
||||
AngularSupportEnabled: angularSupportEnabled,
|
||||
}
|
||||
|
||||
@@ -17,47 +17,57 @@ import (
|
||||
// on the plugins CDN, and it will switch to the correct implementation depending on the plugin and the config.
|
||||
type Service struct {
|
||||
cdn *pluginscdn.Service
|
||||
cfg *config.Cfg
|
||||
}
|
||||
|
||||
func ProvideService(cdn *pluginscdn.Service) *Service {
|
||||
return &Service{cdn: cdn}
|
||||
func ProvideService(cfg *config.Cfg, cdn *pluginscdn.Service) *Service {
|
||||
return &Service{cfg: cfg, cdn: cdn}
|
||||
}
|
||||
|
||||
type PluginInfo struct {
|
||||
pluginJSON plugins.JSONData
|
||||
class plugins.Class
|
||||
dir string
|
||||
}
|
||||
|
||||
func NewPluginInfo(pluginJSON plugins.JSONData, class plugins.Class, fs plugins.FS) PluginInfo {
|
||||
return PluginInfo{
|
||||
pluginJSON: pluginJSON,
|
||||
class: class,
|
||||
dir: fs.Base(),
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultService(cfg *config.Cfg) *Service {
|
||||
return &Service{cdn: pluginscdn.ProvideService(cfg)}
|
||||
return &Service{cfg: cfg, cdn: pluginscdn.ProvideService(cfg)}
|
||||
}
|
||||
|
||||
// Base returns the base path for the specified plugin.
|
||||
func (s *Service) Base(pluginJSON plugins.JSONData, class plugins.Class, pluginDir string) (string, error) {
|
||||
if class == plugins.ClassCore {
|
||||
return path.Join("public/app/plugins", string(pluginJSON.Type), filepath.Base(pluginDir)), nil
|
||||
func (s *Service) Base(n PluginInfo) (string, error) {
|
||||
if n.class == plugins.ClassCore {
|
||||
return path.Join("/", s.cfg.GrafanaAppSubURL, "/public/app/plugins", string(n.pluginJSON.Type), filepath.Base(n.dir)), nil
|
||||
}
|
||||
if s.cdn.PluginSupported(pluginJSON.ID) {
|
||||
return s.cdn.SystemJSAssetPath(pluginJSON.ID, pluginJSON.Info.Version, "")
|
||||
if s.cdn.PluginSupported(n.pluginJSON.ID) {
|
||||
return s.cdn.AssetURL(n.pluginJSON.ID, n.pluginJSON.Info.Version, "")
|
||||
}
|
||||
return path.Join("public/plugins", pluginJSON.ID), nil
|
||||
return path.Join("/", s.cfg.GrafanaAppSubURL, "/public/plugins", n.pluginJSON.ID), nil
|
||||
}
|
||||
|
||||
// Module returns the module.js path for the specified plugin.
|
||||
func (s *Service) Module(pluginJSON plugins.JSONData, class plugins.Class, pluginDir string) (string, error) {
|
||||
if class == plugins.ClassCore {
|
||||
return path.Join("app/plugins", string(pluginJSON.Type), filepath.Base(pluginDir), "module"), nil
|
||||
func (s *Service) Module(n PluginInfo) (string, error) {
|
||||
if n.class == plugins.ClassCore {
|
||||
return path.Join("core:plugin", filepath.Base(n.dir)), nil
|
||||
}
|
||||
if s.cdn.PluginSupported(pluginJSON.ID) {
|
||||
return s.cdn.SystemJSAssetPath(pluginJSON.ID, pluginJSON.Info.Version, "module")
|
||||
if s.cdn.PluginSupported(n.pluginJSON.ID) {
|
||||
return s.cdn.AssetURL(n.pluginJSON.ID, n.pluginJSON.Info.Version, "module.js")
|
||||
}
|
||||
return path.Join("plugins", pluginJSON.ID, "module"), nil
|
||||
return path.Join("/", s.cfg.GrafanaAppSubURL, "/public/plugins", n.pluginJSON.ID, "module.js"), nil
|
||||
}
|
||||
|
||||
// RelativeURL returns the relative URL for an arbitrary plugin asset.
|
||||
// If pathStr is an empty string, defaultStr is returned.
|
||||
func (s *Service) RelativeURL(p *plugins.Plugin, pathStr, defaultStr string) (string, error) {
|
||||
if pathStr == "" {
|
||||
return defaultStr, nil
|
||||
}
|
||||
if s.cdn.PluginSupported(p.ID) {
|
||||
// CDN
|
||||
return s.cdn.NewCDNURLConstructor(p.ID, p.Info.Version).StringPath(pathStr)
|
||||
func (s *Service) RelativeURL(n PluginInfo, pathStr string) (string, error) {
|
||||
if s.cdn.PluginSupported(n.pluginJSON.ID) {
|
||||
return s.cdn.NewCDNURLConstructor(n.pluginJSON.ID, n.pluginJSON.Info.Version).StringPath(pathStr)
|
||||
}
|
||||
// Local
|
||||
u, err := url.Parse(pathStr)
|
||||
@@ -67,9 +77,20 @@ func (s *Service) RelativeURL(p *plugins.Plugin, pathStr, defaultStr string) (st
|
||||
if u.IsAbs() {
|
||||
return pathStr, nil
|
||||
}
|
||||
// is set as default or has already been prefixed with base path
|
||||
if pathStr == defaultStr || strings.HasPrefix(pathStr, p.BaseURL) {
|
||||
|
||||
baseURL, err := s.Base(n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// has already been prefixed with base path
|
||||
if strings.HasPrefix(pathStr, baseURL) {
|
||||
return pathStr, nil
|
||||
}
|
||||
return path.Join(p.BaseURL, pathStr), nil
|
||||
return path.Join(baseURL, pathStr), nil
|
||||
}
|
||||
|
||||
// DefaultLogoPath returns the default logo path for the specified plugin type.
|
||||
func (s *Service) DefaultLogoPath(pluginType plugins.Type) string {
|
||||
return path.Join("/", s.cfg.GrafanaAppSubURL, fmt.Sprintf("/public/img/icn-%s.svg", string(pluginType)))
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package assetpath
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func extPath(pluginID string) string {
|
||||
return "/grafana/data/plugins/" + pluginID
|
||||
func extPath(pluginID string) *fakes.FakePluginFiles {
|
||||
return fakes.NewFakePluginFiles(pluginID)
|
||||
}
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
@@ -33,15 +36,16 @@ func TestService(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
svc := ProvideService(pluginscdn.ProvideService(&config.Cfg{
|
||||
cfg := &config.Cfg{
|
||||
PluginsCDNURLTemplate: tc.cdnBaseURL,
|
||||
PluginSettings: map[string]map[string]string{
|
||||
"one": {"cdn": "true"},
|
||||
"two": {},
|
||||
},
|
||||
}))
|
||||
}
|
||||
svc := ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||
|
||||
const tableOldPath = "/grafana/public/app/plugins/panel/table-old"
|
||||
tableOldFS := fakes.NewFakePluginFiles("/grafana/public/app/plugins/panel/table-old")
|
||||
jsonData := map[string]plugins.JSONData{
|
||||
"table-old": {ID: "table-old", Info: plugins.Info{Version: "1.0.0"}},
|
||||
|
||||
@@ -56,60 +60,110 @@ func TestService(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Base", func(t *testing.T) {
|
||||
base, err := svc.Base(jsonData["one"], plugins.ClassExternal, extPath("one"))
|
||||
base, err := svc.Base(NewPluginInfo(jsonData["one"], plugins.ClassExternal, extPath("one")))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "plugin-cdn/one/1.0.0/public/plugins/one", base)
|
||||
|
||||
base, err = svc.Base(jsonData["two"], plugins.ClassExternal, extPath("two"))
|
||||
u, err := url.JoinPath(tc.cdnBaseURL, "/one/1.0.0/public/plugins/one")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "public/plugins/two", base)
|
||||
require.Equal(t, u, base)
|
||||
|
||||
base, err = svc.Base(jsonData["table-old"], plugins.ClassCore, tableOldPath)
|
||||
base, err = svc.Base(NewPluginInfo(jsonData["two"], plugins.ClassExternal, extPath("two")))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "public/app/plugins/table-old", base)
|
||||
require.Equal(t, "/public/plugins/two", base)
|
||||
|
||||
base, err = svc.Base(NewPluginInfo(jsonData["table-old"], plugins.ClassCore, tableOldFS))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "/public/app/plugins/table-old", base)
|
||||
})
|
||||
|
||||
t.Run("Module", func(t *testing.T) {
|
||||
module, err := svc.Module(jsonData["one"], plugins.ClassExternal, extPath("one"))
|
||||
module, err := svc.Module(NewPluginInfo(jsonData["one"], plugins.ClassExternal, extPath("one")))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "plugin-cdn/one/1.0.0/public/plugins/one/module", module)
|
||||
|
||||
module, err = svc.Module(jsonData["two"], plugins.ClassExternal, extPath("two"))
|
||||
u, err := url.JoinPath(tc.cdnBaseURL, "/one/1.0.0/public/plugins/one/module.js")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "plugins/two/module", module)
|
||||
require.Equal(t, u, module)
|
||||
|
||||
module, err = svc.Module(jsonData["table-old"], plugins.ClassCore, tableOldPath)
|
||||
module, err = svc.Module(NewPluginInfo(jsonData["two"], plugins.ClassExternal, extPath("two")))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "app/plugins/table-old/module", module)
|
||||
require.Equal(t, "/public/plugins/two/module.js", module)
|
||||
|
||||
module, err = svc.Module(NewPluginInfo(jsonData["table-old"], plugins.ClassCore, tableOldFS))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "core:plugin/table-old", module)
|
||||
})
|
||||
|
||||
t.Run("RelativeURL", func(t *testing.T) {
|
||||
pluginsMap := map[string]*plugins.Plugin{
|
||||
"one": {
|
||||
JSONData: plugins.JSONData{ID: "one", Info: plugins.Info{Version: "1.0.0"}},
|
||||
BaseURL: "plugin-cdn/one/1.0.0/public/pluginsMap/one",
|
||||
},
|
||||
"two": {
|
||||
JSONData: plugins.JSONData{ID: "two", Info: plugins.Info{Version: "2.0.0"}},
|
||||
BaseURL: "public/pluginsMap/two",
|
||||
},
|
||||
}
|
||||
u, err := svc.RelativeURL(pluginsMap["one"], "", "default")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "default", u)
|
||||
|
||||
u, err = svc.RelativeURL(pluginsMap["one"], "path/to/file.txt", "default")
|
||||
u, err := svc.RelativeURL(NewPluginInfo(pluginsMap["one"].JSONData, plugins.ClassExternal, extPath("one")), "")
|
||||
require.NoError(t, err)
|
||||
// given an empty path, base URL will be returned
|
||||
baseURL, err := svc.Base(NewPluginInfo(pluginsMap["one"].JSONData, plugins.ClassExternal, extPath("one")))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, baseURL, u)
|
||||
|
||||
u, err = svc.RelativeURL(NewPluginInfo(pluginsMap["one"].JSONData, plugins.ClassExternal, extPath("one")), "path/to/file.txt")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, strings.TrimRight(tc.cdnBaseURL, "/")+"/one/1.0.0/public/plugins/one/path/to/file.txt", u)
|
||||
|
||||
u, err = svc.RelativeURL(pluginsMap["two"], "path/to/file.txt", "default")
|
||||
u, err = svc.RelativeURL(NewPluginInfo(pluginsMap["two"].JSONData, plugins.ClassExternal, extPath("two")), "path/to/file.txt")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "public/pluginsMap/two/path/to/file.txt", u)
|
||||
require.Equal(t, "/public/plugins/two/path/to/file.txt", u)
|
||||
|
||||
u, err = svc.RelativeURL(pluginsMap["two"], "default", "default")
|
||||
u, err = svc.RelativeURL(NewPluginInfo(pluginsMap["two"].JSONData, plugins.ClassExternal, extPath("two")), "default")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "default", u)
|
||||
require.Equal(t, "/public/plugins/two/default", u)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("With App Sub URL", func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
appSubURL string
|
||||
}{
|
||||
{
|
||||
appSubURL: "grafana",
|
||||
},
|
||||
{
|
||||
appSubURL: "/grafana",
|
||||
},
|
||||
{
|
||||
appSubURL: "grafana/",
|
||||
},
|
||||
{
|
||||
appSubURL: "/grafana/",
|
||||
},
|
||||
} {
|
||||
cfg := &config.Cfg{GrafanaAppSubURL: tc.appSubURL}
|
||||
svc := ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||
|
||||
dir := "/plugins/test-datasource"
|
||||
p := plugins.JSONData{ID: "test-datasource"}
|
||||
fs := fakes.NewFakePluginFiles(dir)
|
||||
|
||||
base, err := svc.Base(NewPluginInfo(p, plugins.ClassExternal, fs))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "/grafana/public/plugins/test-datasource", base)
|
||||
|
||||
mod, err := svc.Module(NewPluginInfo(p, plugins.ClassExternal, fs))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "/grafana/public/plugins/test-datasource/module.js", mod)
|
||||
|
||||
base, err = svc.Base(NewPluginInfo(p, plugins.ClassCore, fs))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "/grafana/public/app/plugins/test-datasource", base)
|
||||
|
||||
mod, err = svc.Module(NewPluginInfo(p, plugins.ClassCore, fs))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "core:plugin/test-datasource", mod)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Description: "Data source for Amazon AWS monitoring service",
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
Large: "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
Small: "/public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
Large: "/public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
},
|
||||
},
|
||||
Includes: []*plugins.Includes{
|
||||
@@ -105,8 +105,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
QueryOptions: map[string]bool{"minInterval": true},
|
||||
},
|
||||
Module: "app/plugins/datasource/cloudwatch/module",
|
||||
BaseURL: "public/app/plugins/datasource/cloudwatch",
|
||||
Module: "core:plugin/cloudwatch",
|
||||
BaseURL: "/public/app/plugins/datasource/cloudwatch",
|
||||
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")),
|
||||
Signature: plugins.SignatureStatusInternal,
|
||||
@@ -132,8 +132,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Version: "1.0.0",
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
@@ -145,8 +145,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
State: "alpha",
|
||||
},
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
@@ -171,8 +171,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
URL: "http://test.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/test-app/img/logo_small.png",
|
||||
Large: "public/plugins/test-app/img/logo_large.png",
|
||||
Small: "/public/plugins/test-app/img/logo_small.png",
|
||||
Large: "/public/plugins/test-app/img/logo_large.png",
|
||||
},
|
||||
Links: []plugins.InfoLink{
|
||||
{Name: "Project site", URL: "http://project.com"},
|
||||
@@ -180,8 +180,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Description: "Official Grafana Test App & Dashboard bundle",
|
||||
Screenshots: []plugins.Screenshots{
|
||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
},
|
||||
Version: "1.0.0",
|
||||
Updated: "2015-02-10",
|
||||
@@ -222,8 +222,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/includes-symlinks")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
@@ -249,8 +249,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
URL: "https://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
@@ -262,8 +262,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
Signature: "unsigned",
|
||||
},
|
||||
@@ -295,8 +295,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
URL: "https://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
@@ -308,8 +308,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
},
|
||||
@@ -374,8 +374,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
{Name: "License & Terms", URL: "http://license.com"},
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-app.svg",
|
||||
Large: "public/img/icn-app.svg",
|
||||
Small: "/public/img/icn-app.svg",
|
||||
Large: "/public/img/icn-app.svg",
|
||||
},
|
||||
Updated: "2015-02-10",
|
||||
},
|
||||
@@ -394,8 +394,48 @@ func TestLoader_Load(t *testing.T) {
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/test-app-with-includes")),
|
||||
Class: plugins.ClassExternal,
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Load a plugin with app sub url set",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
DevMode: true,
|
||||
GrafanaAppSubURL: "grafana",
|
||||
},
|
||||
pluginPaths: []string{"../testdata/unsigned-datasource"},
|
||||
want: []*plugins.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-datasource",
|
||||
Type: plugins.TypeDataSource,
|
||||
Name: "Test",
|
||||
Info: plugins.Info{
|
||||
Author: plugins.InfoLink{
|
||||
Name: "Grafana Labs",
|
||||
URL: "https://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "/grafana/public/img/icn-datasource.svg",
|
||||
Large: "/grafana/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
Dependencies: plugins.Dependencies{
|
||||
GrafanaVersion: "*",
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
Backend: true,
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "/grafana/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/grafana/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -48,7 +48,7 @@ func New(cfg *config.Cfg, opts Opts) *Bootstrap {
|
||||
}
|
||||
|
||||
if opts.DecorateFuncs == nil {
|
||||
opts.DecorateFuncs = DefaultDecorateFuncs
|
||||
opts.DecorateFuncs = DefaultDecorateFuncs(cfg)
|
||||
}
|
||||
|
||||
return &Bootstrap{
|
||||
|
||||
@@ -25,27 +25,27 @@ func NewDefaultPluginFactory(assetPath *assetpath.Service) *DefaultPluginFactory
|
||||
|
||||
func (f *DefaultPluginFactory) createPlugin(p plugins.FoundPlugin, class plugins.Class,
|
||||
sig plugins.Signature) (*plugins.Plugin, error) {
|
||||
baseURL, err := f.assetPath.Base(p.JSONData, class, p.FS.Base())
|
||||
info := assetpath.NewPluginInfo(p.JSONData, class, p.FS)
|
||||
baseURL, err := f.assetPath.Base(info)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("base url: %w", err)
|
||||
}
|
||||
moduleURL, err := f.assetPath.Module(p.JSONData, class, p.FS.Base())
|
||||
moduleURL, err := f.assetPath.Module(info)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("module url: %w", err)
|
||||
}
|
||||
|
||||
plugin := &plugins.Plugin{
|
||||
JSONData: p.JSONData,
|
||||
Class: class,
|
||||
FS: p.FS,
|
||||
BaseURL: baseURL,
|
||||
Module: moduleURL,
|
||||
Class: class,
|
||||
Signature: sig.Status,
|
||||
SignatureType: sig.Type,
|
||||
SignatureOrg: sig.SigningOrg,
|
||||
}
|
||||
plugin.SetLogger(log.New(fmt.Sprintf("plugin.%s", plugin.ID)))
|
||||
|
||||
plugin.SetLogger(log.New(fmt.Sprintf("plugin.%s", plugin.ID)))
|
||||
if err = setImages(plugin, f.assetPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -54,23 +54,25 @@ func (f *DefaultPluginFactory) createPlugin(p plugins.FoundPlugin, class plugins
|
||||
}
|
||||
|
||||
func setImages(p *plugins.Plugin, assetPath *assetpath.Service) error {
|
||||
info := assetpath.NewPluginInfo(p.JSONData, p.Class, p.FS)
|
||||
var err error
|
||||
for _, dst := range []*string{&p.Info.Logos.Small, &p.Info.Logos.Large} {
|
||||
*dst, err = assetPath.RelativeURL(p, *dst, defaultLogoPath(p.Type))
|
||||
if len(*dst) == 0 {
|
||||
*dst = assetPath.DefaultLogoPath(p.Type)
|
||||
continue
|
||||
}
|
||||
|
||||
*dst, err = assetPath.RelativeURL(info, *dst)
|
||||
if err != nil {
|
||||
return fmt.Errorf("logo: %w", err)
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(p.Info.Screenshots); i++ {
|
||||
screenshot := &p.Info.Screenshots[i]
|
||||
screenshot.Path, err = assetPath.RelativeURL(p, screenshot.Path, "")
|
||||
screenshot.Path, err = assetPath.RelativeURL(info, screenshot.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("screenshot %d relative url: %w", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultLogoPath(pluginType plugins.Type) string {
|
||||
return fmt.Sprintf("public/img/icn-%s.svg", string(pluginType))
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// DefaultConstructor implements the default ConstructFunc used for the Construct step of the Bootstrap stage.
|
||||
@@ -27,10 +27,12 @@ func DefaultConstructFunc(signatureCalculator plugins.SignatureCalculator, asset
|
||||
}
|
||||
|
||||
// DefaultDecorateFuncs are the default DecorateFuncs used for the Decorate step of the Bootstrap stage.
|
||||
var DefaultDecorateFuncs = []DecorateFunc{
|
||||
AliasDecorateFunc,
|
||||
AppDefaultNavURLDecorateFunc,
|
||||
AppChildDecorateFunc,
|
||||
func DefaultDecorateFuncs(cfg *config.Cfg) []DecorateFunc {
|
||||
return []DecorateFunc{
|
||||
AliasDecorateFunc,
|
||||
AppDefaultNavURLDecorateFunc,
|
||||
AppChildDecorateFunc(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultConstructor returns a new DefaultConstructor.
|
||||
@@ -123,24 +125,26 @@ func setDefaultNavURL(p *plugins.Plugin) {
|
||||
}
|
||||
|
||||
// AppChildDecorateFunc is a DecorateFunc that configures child plugins of app plugins.
|
||||
func AppChildDecorateFunc(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
|
||||
if p.Parent != nil && p.Parent.IsApp() {
|
||||
configureAppChildPlugin(p.Parent, p)
|
||||
func AppChildDecorateFunc(cfg *config.Cfg) DecorateFunc {
|
||||
return func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
|
||||
if p.Parent != nil && p.Parent.IsApp() {
|
||||
configureAppChildPlugin(cfg, p.Parent, p)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func configureAppChildPlugin(parent *plugins.Plugin, child *plugins.Plugin) {
|
||||
func configureAppChildPlugin(cfg *config.Cfg, parent *plugins.Plugin, child *plugins.Plugin) {
|
||||
if !parent.IsApp() {
|
||||
return
|
||||
}
|
||||
appSubPath := strings.ReplaceAll(strings.Replace(child.FS.Base(), parent.FS.Base(), "", 1), "\\", "/")
|
||||
child.IncludedInAppID = parent.ID
|
||||
child.BaseURL = parent.BaseURL
|
||||
|
||||
appSubPath := strings.ReplaceAll(strings.Replace(child.FS.Base(), parent.FS.Base(), "", 1), "\\", "/")
|
||||
if parent.IsCorePlugin() {
|
||||
child.Module = util.JoinURLFragments("app/plugins/app/"+parent.ID, appSubPath) + "/module"
|
||||
child.Module = path.Join("core:plugin", parent.ID, appSubPath)
|
||||
} else {
|
||||
child.Module = util.JoinURLFragments("plugins/"+parent.ID, appSubPath) + "/module"
|
||||
child.Module = path.Join("/", cfg.GrafanaAppSubURL, "/public/plugins", parent.ID, appSubPath, "module.js")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
)
|
||||
@@ -65,7 +66,7 @@ func TestSetDefaultNavURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetPathsBasedOnApp(t *testing.T) {
|
||||
func Test_configureAppChildPlugin(t *testing.T) {
|
||||
t.Run("When setting paths based on core plugin on Windows", func(t *testing.T) {
|
||||
child := &plugins.Plugin{
|
||||
FS: fakes.NewFakePluginFiles("c:\\grafana\\public\\app\\plugins\\app\\testdata-app\\datasources\\datasource"),
|
||||
@@ -77,13 +78,42 @@ func TestSetPathsBasedOnApp(t *testing.T) {
|
||||
},
|
||||
Class: plugins.ClassCore,
|
||||
FS: fakes.NewFakePluginFiles("c:\\grafana\\public\\app\\plugins\\app\\testdata-app"),
|
||||
BaseURL: "public/app/plugins/app/testdata-app",
|
||||
BaseURL: "/public/app/plugins/app/testdata-app",
|
||||
}
|
||||
|
||||
configureAppChildPlugin(parent, child)
|
||||
configureAppChildPlugin(&config.Cfg{}, parent, child)
|
||||
|
||||
require.Equal(t, "app/plugins/app/testdata-app/datasources/datasource/module", child.Module)
|
||||
require.Equal(t, "core:plugin/testdata-app/datasources/datasource", child.Module)
|
||||
require.Equal(t, "testdata-app", child.IncludedInAppID)
|
||||
require.Equal(t, "public/app/plugins/app/testdata-app", child.BaseURL)
|
||||
require.Equal(t, "/public/app/plugins/app/testdata-app", child.BaseURL)
|
||||
|
||||
t.Run("App sub URL has no effect on Core plugins", func(t *testing.T) {
|
||||
configureAppChildPlugin(&config.Cfg{GrafanaAppSubURL: "/grafana"}, parent, child)
|
||||
|
||||
require.Equal(t, "core:plugin/testdata-app/datasources/datasource", child.Module)
|
||||
require.Equal(t, "testdata-app", child.IncludedInAppID)
|
||||
require.Equal(t, "/public/app/plugins/app/testdata-app", child.BaseURL)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("When setting paths based on external plugin with app sub URL", func(t *testing.T) {
|
||||
child := &plugins.Plugin{
|
||||
FS: fakes.NewFakePluginFiles("/plugins/parent-app/child-panel"),
|
||||
}
|
||||
parent := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
Type: plugins.TypeApp,
|
||||
ID: "testdata-app",
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
FS: fakes.NewFakePluginFiles("/plugins/parent-app"),
|
||||
BaseURL: "/grafana/plugins/parent-app",
|
||||
}
|
||||
|
||||
configureAppChildPlugin(&config.Cfg{GrafanaAppSubURL: "/grafana"}, parent, child)
|
||||
|
||||
require.Equal(t, "/grafana/public/plugins/testdata-app/child-panel/module.js", child.Module)
|
||||
require.Equal(t, "testdata-app", child.IncludedInAppID)
|
||||
require.Equal(t, "/grafana/plugins/parent-app", child.BaseURL)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,9 +10,6 @@ import (
|
||||
const (
|
||||
// cdnAssetPathTemplate is the relative path template used to locate plugin CDN assets
|
||||
cdnAssetPathTemplate = "{id}/{version}/public/plugins/{id}/{assetPath}"
|
||||
|
||||
// systemJSCDNURLTemplate is a special path template used by system.js to identify plugin CDN assets
|
||||
systemJSCDNURLTemplate = "plugin-cdn/" + cdnAssetPathTemplate
|
||||
)
|
||||
|
||||
var ErrPluginNotCDN = errors.New("plugin is not a cdn plugin")
|
||||
@@ -57,21 +54,6 @@ func (s *Service) BaseURL() (string, error) {
|
||||
return s.cfg.PluginsCDNURLTemplate, nil
|
||||
}
|
||||
|
||||
// SystemJSAssetPath returns a system-js path for the specified asset on the plugins CDN.
|
||||
// The returned path will follow the template specified in systemJSCDNURLTemplate.
|
||||
// If assetPath is an empty string, the base path for the plugin is returned.
|
||||
func (s *Service) SystemJSAssetPath(pluginID, pluginVersion, assetPath string) (string, error) {
|
||||
u, err := URLConstructor{
|
||||
cdnURLTemplate: systemJSCDNURLTemplate,
|
||||
pluginID: pluginID,
|
||||
pluginVersion: pluginVersion,
|
||||
}.Path(assetPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// AssetURL returns the URL of a CDN asset for a CDN plugin. If the specified plugin is not a CDN plugin,
|
||||
// it returns ErrPluginNotCDN.
|
||||
func (s *Service) AssetURL(pluginID, pluginVersion, assetPath string) (string, error) {
|
||||
|
||||
@@ -41,6 +41,7 @@ func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg, fe
|
||||
grafanaCfg.PluginLogBackendRequests,
|
||||
grafanaCfg.PluginsCDNURLTemplate,
|
||||
grafanaCfg.AppURL,
|
||||
grafanaCfg.AppSubURL,
|
||||
tracingCfg,
|
||||
features,
|
||||
grafanaCfg.AngularSupportEnabled,
|
||||
|
||||
@@ -83,8 +83,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Description: "Data source for Amazon AWS monitoring service",
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
Large: "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
Small: "/public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
Large: "/public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
},
|
||||
},
|
||||
Includes: []*plugins.Includes{
|
||||
@@ -106,8 +106,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
QueryOptions: map[string]bool{"minInterval": true},
|
||||
},
|
||||
Module: "app/plugins/datasource/cloudwatch/module",
|
||||
BaseURL: "public/app/plugins/datasource/cloudwatch",
|
||||
Module: "core:plugin/cloudwatch",
|
||||
BaseURL: "/public/app/plugins/datasource/cloudwatch",
|
||||
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(corePluginDir(t), "app/plugins/datasource/cloudwatch")),
|
||||
Signature: plugins.SignatureStatusInternal,
|
||||
@@ -133,8 +133,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Version: "1.0.0",
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
@@ -146,8 +146,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
State: "alpha",
|
||||
},
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "valid-v2-signature/plugin/")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
@@ -172,8 +172,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
URL: "http://test.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/test-app/img/logo_small.png",
|
||||
Large: "public/plugins/test-app/img/logo_large.png",
|
||||
Small: "/public/plugins/test-app/img/logo_small.png",
|
||||
Large: "/public/plugins/test-app/img/logo_large.png",
|
||||
},
|
||||
Links: []plugins.InfoLink{
|
||||
{Name: "Project site", URL: "http://project.com"},
|
||||
@@ -181,8 +181,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Description: "Official Grafana Test App & Dashboard bundle",
|
||||
Screenshots: []plugins.Screenshots{
|
||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
},
|
||||
Version: "1.0.0",
|
||||
Updated: "2015-02-10",
|
||||
@@ -223,8 +223,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "includes-symlinks")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
@@ -250,8 +250,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
URL: "https://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
@@ -263,8 +263,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "unsigned-datasource/plugin")),
|
||||
Signature: "unsigned",
|
||||
},
|
||||
@@ -301,8 +301,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
URL: "https://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
@@ -314,8 +314,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "unsigned-datasource/plugin")),
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
},
|
||||
@@ -403,8 +403,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
{Name: "License & Terms", URL: "http://license.com"},
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-app.svg",
|
||||
Large: "public/img/icn-app.svg",
|
||||
Small: "/public/img/icn-app.svg",
|
||||
Large: "/public/img/icn-app.svg",
|
||||
},
|
||||
Updated: "2015-02-10",
|
||||
},
|
||||
@@ -423,8 +423,48 @@ func TestLoader_Load(t *testing.T) {
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "test-app-with-includes")),
|
||||
Class: plugins.ClassExternal,
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Load a plugin with app sub url set",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
DevMode: true,
|
||||
GrafanaAppSubURL: "grafana",
|
||||
},
|
||||
pluginPaths: []string{filepath.Join(testDataDir(t), "unsigned-datasource")},
|
||||
want: []*plugins.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-datasource",
|
||||
Type: plugins.TypeDataSource,
|
||||
Name: "Test",
|
||||
Info: plugins.Info{
|
||||
Author: plugins.InfoLink{
|
||||
Name: "Grafana Labs",
|
||||
URL: "https://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "/grafana/public/img/icn-datasource.svg",
|
||||
Large: "/grafana/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
},
|
||||
Dependencies: plugins.Dependencies{
|
||||
GrafanaVersion: "*",
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
Backend: true,
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "/grafana/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/grafana/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "unsigned-datasource/plugin")),
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -478,8 +518,8 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
||||
},
|
||||
Version: "1.0.0",
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/grafana-test-datasource/img/ds.svg",
|
||||
Large: "public/plugins/grafana-test-datasource/img/ds.svg",
|
||||
Small: "/public/plugins/grafana-test-datasource/img/ds.svg",
|
||||
Large: "/public/plugins/grafana-test-datasource/img/ds.svg",
|
||||
},
|
||||
Updated: "2023-08-03",
|
||||
Screenshots: []plugins.Screenshots{},
|
||||
@@ -513,8 +553,8 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
||||
FS: mustNewStaticFSForTests(t, pluginPaths[0]),
|
||||
Class: plugins.ClassExternal,
|
||||
Signature: plugins.SignatureStatusUnsigned,
|
||||
Module: "plugins/grafana-test-datasource/module",
|
||||
BaseURL: "public/plugins/grafana-test-datasource",
|
||||
Module: "/public/plugins/grafana-test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/grafana-test-datasource",
|
||||
ExternalService: &oauth.ExternalService{
|
||||
ClientID: "client-id",
|
||||
ClientSecret: "secretz",
|
||||
@@ -613,8 +653,8 @@ func TestLoader_Load_CustomSource(t *testing.T) {
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "cdn/plugin")),
|
||||
Class: plugins.ClassBundled,
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
BaseURL: "plugin-cdn/grafana-worldmap-panel/0.3.3/public/plugins/grafana-worldmap-panel",
|
||||
Module: "plugin-cdn/grafana-worldmap-panel/0.3.3/public/plugins/grafana-worldmap-panel/module",
|
||||
BaseURL: "https://cdn.example.com/grafana-worldmap-panel/0.3.3/public/plugins/grafana-worldmap-panel",
|
||||
Module: "https://cdn.example.com/grafana-worldmap-panel/0.3.3/public/plugins/grafana-worldmap-panel/module.js",
|
||||
}}
|
||||
|
||||
l := newLoader(t, cfg, fakes.NewFakePluginRegistry(), fakes.NewFakeProcessManager(), fakes.NewFakeBackendProcessProvider(), newFakeSignatureErrorTracker())
|
||||
@@ -671,8 +711,8 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
URL: "https://willbrowne.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Test",
|
||||
Version: "1.0.0",
|
||||
@@ -686,8 +726,8 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
State: plugins.ReleaseStateAlpha,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "valid-v2-pvt-signature/plugin")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.SignatureTypePrivate,
|
||||
@@ -766,8 +806,8 @@ func TestLoader_Load_RBACReady(t *testing.T) {
|
||||
Version: "1.0.0",
|
||||
Links: []plugins.InfoLink{},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-app.svg",
|
||||
Large: "public/img/icn-app.svg",
|
||||
Small: "/public/img/icn-app.svg",
|
||||
Large: "/public/img/icn-app.svg",
|
||||
},
|
||||
Updated: "2015-02-10",
|
||||
},
|
||||
@@ -798,8 +838,8 @@ func TestLoader_Load_RBACReady(t *testing.T) {
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
SignatureType: plugins.SignatureTypePrivate,
|
||||
SignatureOrg: "gabrielmabille",
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -843,8 +883,8 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
|
||||
Author: plugins.InfoLink{Name: "Will Browne", URL: "https://willbrowne.com"},
|
||||
Description: "Test",
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Version: "1.0.0",
|
||||
},
|
||||
@@ -858,8 +898,8 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
SignatureType: plugins.SignatureTypePrivate,
|
||||
SignatureOrg: "Will Browne",
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -905,12 +945,12 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
|
||||
{Name: "License & Terms", URL: "http://license.com"},
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/test-app/img/logo_small.png",
|
||||
Large: "public/plugins/test-app/img/logo_large.png",
|
||||
Small: "/public/plugins/test-app/img/logo_small.png",
|
||||
Large: "/public/plugins/test-app/img/logo_large.png",
|
||||
},
|
||||
Screenshots: []plugins.Screenshots{
|
||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
},
|
||||
Updated: "2015-02-10",
|
||||
},
|
||||
@@ -934,8 +974,8 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
SignatureOrg: "Grafana Labs",
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -985,12 +1025,12 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
|
||||
{Name: "License & Terms", URL: "http://license.com"},
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/test-app/img/logo_small.png",
|
||||
Large: "public/plugins/test-app/img/logo_large.png",
|
||||
Small: "/public/plugins/test-app/img/logo_small.png",
|
||||
Large: "/public/plugins/test-app/img/logo_large.png",
|
||||
},
|
||||
Screenshots: []plugins.Screenshots{
|
||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
||||
{Path: "/public/plugins/test-app/img/screenshot2.png", Name: "img2"},
|
||||
},
|
||||
Updated: "2015-02-10",
|
||||
},
|
||||
@@ -1014,8 +1054,8 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
SignatureOrg: "Grafana Labs",
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
Module: "/public/plugins/test-app/module.js",
|
||||
BaseURL: "/public/plugins/test-app",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1165,8 +1205,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
URL: "http://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-datasource.svg",
|
||||
Large: "public/img/icn-datasource.svg",
|
||||
Small: "/public/img/icn-datasource.svg",
|
||||
Large: "/public/img/icn-datasource.svg",
|
||||
},
|
||||
Description: "Parent plugin",
|
||||
Version: "1.0.0",
|
||||
@@ -1178,8 +1218,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
Backend: true,
|
||||
},
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
Module: "/public/plugins/test-datasource/module.js",
|
||||
BaseURL: "/public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "nested-plugins/parent")),
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
@@ -1198,8 +1238,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
URL: "http://grafana.com",
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-panel.svg",
|
||||
Large: "public/img/icn-panel.svg",
|
||||
Small: "/public/img/icn-panel.svg",
|
||||
Large: "/public/img/icn-panel.svg",
|
||||
},
|
||||
Description: "Child plugin",
|
||||
Version: "1.0.1",
|
||||
@@ -1210,8 +1250,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
},
|
||||
Module: "plugins/test-panel/module",
|
||||
BaseURL: "public/plugins/test-panel",
|
||||
Module: "/public/plugins/test-panel/module.js",
|
||||
BaseURL: "/public/plugins/test-panel",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "nested-plugins/parent/nested")),
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
SignatureType: plugins.SignatureTypeGrafana,
|
||||
@@ -1290,8 +1330,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
{Name: "License", URL: "https://github.com/grafana/grafana-starter-app/blob/master/LICENSE"},
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/myorgid-simple-app/img/logo.svg",
|
||||
Large: "public/plugins/myorgid-simple-app/img/logo.svg",
|
||||
Small: "/public/plugins/myorgid-simple-app/img/logo.svg",
|
||||
Large: "/public/plugins/myorgid-simple-app/img/logo.svg",
|
||||
},
|
||||
Screenshots: []plugins.Screenshots{},
|
||||
Description: "Grafana App Plugin Template",
|
||||
@@ -1346,8 +1386,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
Backend: false,
|
||||
},
|
||||
Module: "plugins/myorgid-simple-app/module",
|
||||
BaseURL: "public/plugins/myorgid-simple-app",
|
||||
Module: "/public/plugins/myorgid-simple-app/module.js",
|
||||
BaseURL: "/public/plugins/myorgid-simple-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "app-with-child/dist")),
|
||||
DefaultNavURL: "/plugins/myorgid-simple-app/page/root-page-react",
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
@@ -1370,8 +1410,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
{Name: "License", URL: "https://github.com/grafana/grafana-starter-panel/blob/master/LICENSE"},
|
||||
},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/plugins/myorgid-simple-panel/img/logo.svg",
|
||||
Large: "public/plugins/myorgid-simple-panel/img/logo.svg",
|
||||
Small: "/public/plugins/myorgid-simple-panel/img/logo.svg",
|
||||
Large: "/public/plugins/myorgid-simple-panel/img/logo.svg",
|
||||
},
|
||||
Screenshots: []plugins.Screenshots{},
|
||||
Description: "Grafana Panel Plugin Template",
|
||||
@@ -1384,8 +1424,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
},
|
||||
Module: "plugins/myorgid-simple-app/child/module",
|
||||
BaseURL: "public/plugins/myorgid-simple-app",
|
||||
Module: "/public/plugins/myorgid-simple-app/child/module.js",
|
||||
BaseURL: "/public/plugins/myorgid-simple-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "app-with-child/dist/child")),
|
||||
IncludedInAppID: parent.ID,
|
||||
Signature: plugins.SignatureStatusValid,
|
||||
@@ -1434,7 +1474,7 @@ type loaderDepOpts struct {
|
||||
|
||||
func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process.Manager,
|
||||
backendFactory plugins.BackendFactoryProvider, sigErrTracker pluginerrs.SignatureErrorTracker) *Loader {
|
||||
assets := assetpath.ProvideService(pluginscdn.ProvideService(cfg))
|
||||
assets := assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||
lic := fakes.NewFakeLicensingService()
|
||||
angularInspector := angularinspector.NewStaticInspector()
|
||||
|
||||
@@ -1449,7 +1489,7 @@ func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process
|
||||
}
|
||||
|
||||
func newLoaderWithOpts(t *testing.T, cfg *config.Cfg, opts loaderDepOpts) *Loader {
|
||||
assets := assetpath.ProvideService(pluginscdn.ProvideService(cfg))
|
||||
assets := assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||
lic := fakes.NewFakeLicensingService()
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
proc := fakes.NewFakeProcessManager()
|
||||
|
||||
@@ -37,7 +37,7 @@ func ProvideDiscoveryStage(cfg *config.Cfg, pf finder.Finder, pr registry.Servic
|
||||
func ProvideBootstrapStage(cfg *config.Cfg, sc plugins.SignatureCalculator, a *assetpath.Service) *bootstrap.Bootstrap {
|
||||
return bootstrap.New(cfg, bootstrap.Opts{
|
||||
ConstructFunc: bootstrap.DefaultConstructFunc(sc, a),
|
||||
DecorateFuncs: bootstrap.DefaultDecorateFuncs,
|
||||
DecorateFuncs: bootstrap.DefaultDecorateFuncs(cfg),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ func CreateIntegrationTestCtx(t *testing.T, cfg *setting.Cfg, coreRegistry *core
|
||||
errTracker := pluginerrs.ProvideSignatureErrorTracker()
|
||||
|
||||
disc := pipeline.ProvideDiscoveryStage(pCfg, finder.NewLocalFinder(true), reg)
|
||||
boot := pipeline.ProvideBootstrapStage(pCfg, signature.ProvideService(pCfg, statickey.New()), assetpath.ProvideService(cdn))
|
||||
boot := pipeline.ProvideBootstrapStage(pCfg, signature.ProvideService(pCfg, statickey.New()), assetpath.ProvideService(pCfg, cdn))
|
||||
valid := pipeline.ProvideValidationStage(pCfg, signature.NewValidator(signature.NewUnsignedAuthorizer(pCfg)), angularInspector, errTracker)
|
||||
init := pipeline.ProvideInitializationStage(pCfg, reg, fakes.NewFakeLicensingService(), provider.ProvideService(coreRegistry), proc, &fakes.FakeOauthService{}, fakes.NewFakeRoleRegistry())
|
||||
term, err := pipeline.ProvideTerminationStage(pCfg, reg, proc)
|
||||
@@ -90,7 +90,7 @@ func CreateTestLoader(t *testing.T, cfg *pluginsCfg.Cfg, opts LoaderOpts) *loade
|
||||
}
|
||||
|
||||
if opts.Bootstrapper == nil {
|
||||
opts.Bootstrapper = pipeline.ProvideBootstrapStage(cfg, signature.ProvideService(cfg, statickey.New()), assetpath.ProvideService(pluginscdn.ProvideService(cfg)))
|
||||
opts.Bootstrapper = pipeline.ProvideBootstrapStage(cfg, signature.ProvideService(cfg, statickey.New()), assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg)))
|
||||
}
|
||||
|
||||
if opts.Validator == nil {
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"description": "Shows list of alerts and their current status",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg",
|
||||
"large": "public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg"
|
||||
"small": "/public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg",
|
||||
"large": "/public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -55,8 +55,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/alertmanager/img/logo.svg",
|
||||
"large": "public/app/plugins/datasource/alertmanager/img/logo.svg"
|
||||
"small": "/public/app/plugins/datasource/alertmanager/img/logo.svg",
|
||||
"large": "/public/app/plugins/datasource/alertmanager/img/logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -92,8 +92,8 @@
|
||||
"description": "List annotations",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/annolist/img/icn-annolist-panel.svg",
|
||||
"large": "public/app/plugins/panel/annolist/img/icn-annolist-panel.svg"
|
||||
"small": "/public/app/plugins/panel/annolist/img/icn-annolist-panel.svg",
|
||||
"large": "/public/app/plugins/panel/annolist/img/icn-annolist-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -138,22 +138,22 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/azuremonitor/img/logo.jpg",
|
||||
"large": "public/app/plugins/datasource/azuremonitor/img/logo.jpg"
|
||||
"small": "/public/app/plugins/datasource/azuremonitor/img/logo.jpg",
|
||||
"large": "/public/app/plugins/datasource/azuremonitor/img/logo.jpg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": [
|
||||
{
|
||||
"name": "Azure Contoso Loans",
|
||||
"path": "public/app/plugins/datasource/azuremonitor/img/contoso_loans_grafana_dashboard.png"
|
||||
"path": "/public/app/plugins/datasource/azuremonitor/img/contoso_loans_grafana_dashboard.png"
|
||||
},
|
||||
{
|
||||
"name": "Azure Monitor Network",
|
||||
"path": "public/app/plugins/datasource/azuremonitor/img/azure_monitor_network.png"
|
||||
"path": "/public/app/plugins/datasource/azuremonitor/img/azure_monitor_network.png"
|
||||
},
|
||||
{
|
||||
"name": "Azure Monitor CPU",
|
||||
"path": "public/app/plugins/datasource/azuremonitor/img/azure_monitor_cpu.png"
|
||||
"path": "/public/app/plugins/datasource/azuremonitor/img/azure_monitor_cpu.png"
|
||||
}
|
||||
],
|
||||
"version": "1.0.0",
|
||||
@@ -188,8 +188,8 @@
|
||||
"description": "Categorical charts with group support",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/barchart/img/barchart.svg",
|
||||
"large": "public/app/plugins/panel/barchart/img/barchart.svg"
|
||||
"small": "/public/app/plugins/panel/barchart/img/barchart.svg",
|
||||
"large": "/public/app/plugins/panel/barchart/img/barchart.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -225,8 +225,8 @@
|
||||
"description": "Horizontal and vertical gauges",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/bargauge/img/icon_bar_gauge.svg",
|
||||
"large": "public/app/plugins/panel/bargauge/img/icon_bar_gauge.svg"
|
||||
"small": "/public/app/plugins/panel/bargauge/img/icon_bar_gauge.svg",
|
||||
"large": "/public/app/plugins/panel/bargauge/img/icon_bar_gauge.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -262,8 +262,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/candlestick/img/candlestick.svg",
|
||||
"large": "public/app/plugins/panel/candlestick/img/candlestick.svg"
|
||||
"small": "/public/app/plugins/panel/candlestick/img/candlestick.svg",
|
||||
"large": "/public/app/plugins/panel/candlestick/img/candlestick.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -299,8 +299,8 @@
|
||||
"description": "Explicit element placement",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/canvas/img/icn-canvas.svg",
|
||||
"large": "public/app/plugins/panel/canvas/img/icn-canvas.svg"
|
||||
"small": "/public/app/plugins/panel/canvas/img/icn-canvas.svg",
|
||||
"large": "/public/app/plugins/panel/canvas/img/icn-canvas.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -336,8 +336,8 @@
|
||||
"description": "Data source for Amazon AWS monitoring service",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"large": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png"
|
||||
"small": "/public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"large": "/public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -373,8 +373,8 @@
|
||||
"description": "List of dynamic links to other dashboards",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/dashlist/img/icn-dashlist-panel.svg",
|
||||
"large": "public/app/plugins/panel/dashlist/img/icn-dashlist-panel.svg"
|
||||
"small": "/public/app/plugins/panel/dashlist/img/icn-dashlist-panel.svg",
|
||||
"large": "/public/app/plugins/panel/dashlist/img/icn-dashlist-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -410,8 +410,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/datagrid/img/icn-table-panel.svg",
|
||||
"large": "public/app/plugins/panel/datagrid/img/icn-table-panel.svg"
|
||||
"small": "/public/app/plugins/panel/datagrid/img/icn-table-panel.svg",
|
||||
"large": "/public/app/plugins/panel/datagrid/img/icn-table-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -452,8 +452,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg",
|
||||
"large": "public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg"
|
||||
"small": "/public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg",
|
||||
"large": "/public/app/plugins/datasource/elasticsearch/img/elasticsearch.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -489,8 +489,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/flamegraph/img/icn-flamegraph.svg",
|
||||
"large": "public/app/plugins/panel/flamegraph/img/icn-flamegraph.svg"
|
||||
"small": "/public/app/plugins/panel/flamegraph/img/icn-flamegraph.svg",
|
||||
"large": "/public/app/plugins/panel/flamegraph/img/icn-flamegraph.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -526,8 +526,8 @@
|
||||
"description": "Standard gauge visualization",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/gauge/img/icon_gauge.svg",
|
||||
"large": "public/app/plugins/panel/gauge/img/icon_gauge.svg"
|
||||
"small": "/public/app/plugins/panel/gauge/img/icon_gauge.svg",
|
||||
"large": "/public/app/plugins/panel/gauge/img/icon_gauge.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -563,8 +563,8 @@
|
||||
"description": "Geomap panel",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/geomap/img/icn-geomap.svg",
|
||||
"large": "public/app/plugins/panel/geomap/img/icn-geomap.svg"
|
||||
"small": "/public/app/plugins/panel/geomap/img/icn-geomap.svg",
|
||||
"large": "/public/app/plugins/panel/geomap/img/icn-geomap.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -600,8 +600,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/gettingstarted/img/icn-dashlist-panel.svg",
|
||||
"large": "public/app/plugins/panel/gettingstarted/img/icn-dashlist-panel.svg"
|
||||
"small": "/public/app/plugins/panel/gettingstarted/img/icn-dashlist-panel.svg",
|
||||
"large": "/public/app/plugins/panel/gettingstarted/img/icn-dashlist-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -637,8 +637,8 @@
|
||||
"description": "Data source for Google's monitoring service (formerly named Stackdriver)",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/cloud-monitoring/img/cloud_monitoring_logo.svg",
|
||||
"large": "public/app/plugins/datasource/cloud-monitoring/img/cloud_monitoring_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/cloud-monitoring/img/cloud_monitoring_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/cloud-monitoring/img/cloud_monitoring_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -679,8 +679,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/grafana-pyroscope-datasource/img/grafana_pyroscope_icon.svg",
|
||||
"large": "public/app/plugins/datasource/grafana-pyroscope-datasource/img/grafana_pyroscope_icon.svg"
|
||||
"small": "/public/app/plugins/datasource/grafana-pyroscope-datasource/img/grafana_pyroscope_icon.svg",
|
||||
"large": "/public/app/plugins/datasource/grafana-pyroscope-datasource/img/grafana_pyroscope_icon.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -716,8 +716,8 @@
|
||||
"description": "The old default graph panel",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/graph/img/icn-graph-panel.svg",
|
||||
"large": "public/app/plugins/panel/graph/img/icn-graph-panel.svg"
|
||||
"small": "/public/app/plugins/panel/graph/img/icn-graph-panel.svg",
|
||||
"large": "/public/app/plugins/panel/graph/img/icn-graph-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -762,8 +762,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/graphite/img/graphite_logo.png",
|
||||
"large": "public/app/plugins/datasource/graphite/img/graphite_logo.png"
|
||||
"small": "/public/app/plugins/datasource/graphite/img/graphite_logo.png",
|
||||
"large": "/public/app/plugins/datasource/graphite/img/graphite_logo.png"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -799,8 +799,8 @@
|
||||
"description": "Like a histogram over time",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg",
|
||||
"large": "public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg"
|
||||
"small": "/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg",
|
||||
"large": "/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -836,8 +836,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/histogram/img/histogram.svg",
|
||||
"large": "public/app/plugins/panel/histogram/img/histogram.svg"
|
||||
"small": "/public/app/plugins/panel/histogram/img/histogram.svg",
|
||||
"large": "/public/app/plugins/panel/histogram/img/histogram.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -873,8 +873,8 @@
|
||||
"description": "Open source time series database",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/influxdb/img/influxdb_logo.svg",
|
||||
"large": "public/app/plugins/datasource/influxdb/img/influxdb_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/influxdb/img/influxdb_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/influxdb/img/influxdb_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -919,8 +919,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/jaeger/img/jaeger_logo.svg",
|
||||
"large": "public/app/plugins/datasource/jaeger/img/jaeger_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/jaeger/img/jaeger_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/jaeger/img/jaeger_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -956,8 +956,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/logs/img/icn-logs-panel.svg",
|
||||
"large": "public/app/plugins/panel/logs/img/icn-logs-panel.svg"
|
||||
"small": "/public/app/plugins/panel/logs/img/icn-logs-panel.svg",
|
||||
"large": "/public/app/plugins/panel/logs/img/icn-logs-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1002,8 +1002,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/loki/img/loki_icon.svg",
|
||||
"large": "public/app/plugins/datasource/loki/img/loki_icon.svg"
|
||||
"small": "/public/app/plugins/datasource/loki/img/loki_icon.svg",
|
||||
"large": "/public/app/plugins/datasource/loki/img/loki_icon.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1039,8 +1039,8 @@
|
||||
"description": "Data source for Microsoft SQL Server compatible databases",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/mssql/img/sql_server_logo.svg",
|
||||
"large": "public/app/plugins/datasource/mssql/img/sql_server_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/mssql/img/sql_server_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/mssql/img/sql_server_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1076,8 +1076,8 @@
|
||||
"description": "Data source for MySQL databases",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/mysql/img/mysql_logo.svg",
|
||||
"large": "public/app/plugins/datasource/mysql/img/mysql_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/mysql/img/mysql_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/mysql/img/mysql_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1113,8 +1113,8 @@
|
||||
"description": "RSS feed reader",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/news/img/news.svg",
|
||||
"large": "public/app/plugins/panel/news/img/news.svg"
|
||||
"small": "/public/app/plugins/panel/news/img/news.svg",
|
||||
"large": "/public/app/plugins/panel/news/img/news.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1150,8 +1150,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/nodeGraph/img/icn-node-graph.svg",
|
||||
"large": "public/app/plugins/panel/nodeGraph/img/icn-node-graph.svg"
|
||||
"small": "/public/app/plugins/panel/nodeGraph/img/icn-node-graph.svg",
|
||||
"large": "/public/app/plugins/panel/nodeGraph/img/icn-node-graph.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1187,8 +1187,8 @@
|
||||
"description": "Open source time series database",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/opentsdb/img/opentsdb_logo.png",
|
||||
"large": "public/app/plugins/datasource/opentsdb/img/opentsdb_logo.png"
|
||||
"small": "/public/app/plugins/datasource/opentsdb/img/opentsdb_logo.png",
|
||||
"large": "/public/app/plugins/datasource/opentsdb/img/opentsdb_logo.png"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1229,8 +1229,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/parca/img/logo-small.svg",
|
||||
"large": "public/app/plugins/datasource/parca/img/logo-small.svg"
|
||||
"small": "/public/app/plugins/datasource/parca/img/logo-small.svg",
|
||||
"large": "/public/app/plugins/datasource/parca/img/logo-small.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1266,8 +1266,8 @@
|
||||
"description": "The new core pie chart visualization",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/piechart/img/icon_piechart.svg",
|
||||
"large": "public/app/plugins/panel/piechart/img/icon_piechart.svg"
|
||||
"small": "/public/app/plugins/panel/piechart/img/icon_piechart.svg",
|
||||
"large": "/public/app/plugins/panel/piechart/img/icon_piechart.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1303,8 +1303,8 @@
|
||||
"description": "Data source for PostgreSQL and compatible databases",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg",
|
||||
"large": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/postgres/img/postgresql_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/postgres/img/postgresql_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1345,8 +1345,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/prometheus/img/prometheus_logo.svg",
|
||||
"large": "public/app/plugins/datasource/prometheus/img/prometheus_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1382,8 +1382,8 @@
|
||||
"description": "Big stat values \u0026 sparklines",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/stat/img/icn-singlestat-panel.svg",
|
||||
"large": "public/app/plugins/panel/stat/img/icn-singlestat-panel.svg"
|
||||
"small": "/public/app/plugins/panel/stat/img/icn-singlestat-panel.svg",
|
||||
"large": "/public/app/plugins/panel/stat/img/icn-singlestat-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1419,8 +1419,8 @@
|
||||
"description": "State changes and durations",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/state-timeline/img/timeline.svg",
|
||||
"large": "public/app/plugins/panel/state-timeline/img/timeline.svg"
|
||||
"small": "/public/app/plugins/panel/state-timeline/img/timeline.svg",
|
||||
"large": "/public/app/plugins/panel/state-timeline/img/timeline.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1456,8 +1456,8 @@
|
||||
"description": "Periodic status history",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/status-history/img/status.svg",
|
||||
"large": "public/app/plugins/panel/status-history/img/status.svg"
|
||||
"small": "/public/app/plugins/panel/status-history/img/status.svg",
|
||||
"large": "/public/app/plugins/panel/status-history/img/status.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1493,8 +1493,8 @@
|
||||
"description": "Supports many column styles",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/table/img/icn-table-panel.svg",
|
||||
"large": "public/app/plugins/panel/table/img/icn-table-panel.svg"
|
||||
"small": "/public/app/plugins/panel/table/img/icn-table-panel.svg",
|
||||
"large": "/public/app/plugins/panel/table/img/icn-table-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1530,8 +1530,8 @@
|
||||
"description": "Table Panel for Grafana",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/table-old/img/icn-table-panel.svg",
|
||||
"large": "public/app/plugins/panel/table-old/img/icn-table-panel.svg"
|
||||
"small": "/public/app/plugins/panel/table-old/img/icn-table-panel.svg",
|
||||
"large": "/public/app/plugins/panel/table-old/img/icn-table-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1572,8 +1572,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/tempo/img/tempo_logo.svg",
|
||||
"large": "public/app/plugins/datasource/tempo/img/tempo_logo.svg"
|
||||
"small": "/public/app/plugins/datasource/tempo/img/tempo_logo.svg",
|
||||
"large": "/public/app/plugins/datasource/tempo/img/tempo_logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1609,8 +1609,8 @@
|
||||
"description": "Generates test data in different forms",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/testdata/img/testdata.svg",
|
||||
"large": "public/app/plugins/datasource/testdata/img/testdata.svg"
|
||||
"small": "/public/app/plugins/datasource/testdata/img/testdata.svg",
|
||||
"large": "/public/app/plugins/datasource/testdata/img/testdata.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1646,8 +1646,8 @@
|
||||
"description": "Supports markdown and html content",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/text/img/icn-text-panel.svg",
|
||||
"large": "public/app/plugins/panel/text/img/icn-text-panel.svg"
|
||||
"small": "/public/app/plugins/panel/text/img/icn-text-panel.svg",
|
||||
"large": "/public/app/plugins/panel/text/img/icn-text-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1683,8 +1683,8 @@
|
||||
"description": "Time based line, area and bar charts",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/timeseries/img/icn-timeseries-panel.svg",
|
||||
"large": "public/app/plugins/panel/timeseries/img/icn-timeseries-panel.svg"
|
||||
"small": "/public/app/plugins/panel/timeseries/img/icn-timeseries-panel.svg",
|
||||
"large": "/public/app/plugins/panel/timeseries/img/icn-timeseries-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1720,8 +1720,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/traces/img/traces-panel.svg",
|
||||
"large": "public/app/plugins/panel/traces/img/traces-panel.svg"
|
||||
"small": "/public/app/plugins/panel/traces/img/traces-panel.svg",
|
||||
"large": "/public/app/plugins/panel/traces/img/traces-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1757,8 +1757,8 @@
|
||||
"description": "Like timeseries, but when x != time",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/trend/img/trend.svg",
|
||||
"large": "public/app/plugins/panel/trend/img/trend.svg"
|
||||
"small": "/public/app/plugins/panel/trend/img/trend.svg",
|
||||
"large": "/public/app/plugins/panel/trend/img/trend.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1794,8 +1794,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/welcome/img/icn-dashlist-panel.svg",
|
||||
"large": "public/app/plugins/panel/welcome/img/icn-dashlist-panel.svg"
|
||||
"small": "/public/app/plugins/panel/welcome/img/icn-dashlist-panel.svg",
|
||||
"large": "/public/app/plugins/panel/welcome/img/icn-dashlist-panel.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1831,8 +1831,8 @@
|
||||
"description": "",
|
||||
"links": null,
|
||||
"logos": {
|
||||
"small": "public/app/plugins/panel/xychart/img/icn-xychart.svg",
|
||||
"large": "public/app/plugins/panel/xychart/img/icn-xychart.svg"
|
||||
"small": "/public/app/plugins/panel/xychart/img/icn-xychart.svg",
|
||||
"large": "/public/app/plugins/panel/xychart/img/icn-xychart.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
@@ -1873,8 +1873,8 @@
|
||||
}
|
||||
],
|
||||
"logos": {
|
||||
"small": "public/app/plugins/datasource/zipkin/img/zipkin-logo.svg",
|
||||
"large": "public/app/plugins/datasource/zipkin/img/zipkin-logo.svg"
|
||||
"small": "/public/app/plugins/datasource/zipkin/img/zipkin-logo.svg",
|
||||
"large": "/public/app/plugins/datasource/zipkin/img/zipkin-logo.svg"
|
||||
},
|
||||
"build": {},
|
||||
"screenshots": null,
|
||||
|
||||
Reference in New Issue
Block a user