mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Angular deprecation: Detect Angular plugins and expose in API (#66824)
* Plugins: Angular deprecation: Detect Angular plugins and expose in API * Plugins: Angular detector: Close module.js * Plugins: Angular detector: consistent error messages * Plugins: Angular detector: Add test for missing module.js * Plugins: Angular detector: Fix integration tests * Plugins: Angular detector: Changed Angular detection patterns * Moved inMemoryFS to test_utils.go * Add different angular detectors * Plugins: Update plugins/data/expectedListResp.json * Plugins: Rename angular property to angularDetected * Plugins: Rename angular to angularDetected in Plugin and PluginDTO * Plugins: Add angularDetected to datasources, apps and plugins frontendsettings * Plugins: Add test for AngularDetected frontend settings
This commit is contained in:
parent
4310f574db
commit
16359c82a2
@ -29,22 +29,23 @@ type PluginSetting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PluginListItem struct {
|
type PluginListItem struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Pinned bool `json:"pinned"`
|
Pinned bool `json:"pinned"`
|
||||||
Info plugins.Info `json:"info"`
|
Info plugins.Info `json:"info"`
|
||||||
Dependencies plugins.Dependencies `json:"dependencies"`
|
Dependencies plugins.Dependencies `json:"dependencies"`
|
||||||
LatestVersion string `json:"latestVersion"`
|
LatestVersion string `json:"latestVersion"`
|
||||||
HasUpdate bool `json:"hasUpdate"`
|
HasUpdate bool `json:"hasUpdate"`
|
||||||
DefaultNavUrl string `json:"defaultNavUrl"`
|
DefaultNavUrl string `json:"defaultNavUrl"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
State plugins.ReleaseState `json:"state"`
|
State plugins.ReleaseState `json:"state"`
|
||||||
Signature plugins.SignatureStatus `json:"signature"`
|
Signature plugins.SignatureStatus `json:"signature"`
|
||||||
SignatureType plugins.SignatureType `json:"signatureType"`
|
SignatureType plugins.SignatureType `json:"signatureType"`
|
||||||
SignatureOrg string `json:"signatureOrg"`
|
SignatureOrg string `json:"signatureOrg"`
|
||||||
AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"`
|
AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"`
|
||||||
|
AngularDetected bool `json:"angularDetected"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PluginList []PluginListItem
|
type PluginList []PluginListItem
|
||||||
|
@ -64,16 +64,17 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
|||||||
}
|
}
|
||||||
|
|
||||||
panels[panel.ID] = plugins.PanelDTO{
|
panels[panel.ID] = plugins.PanelDTO{
|
||||||
ID: panel.ID,
|
ID: panel.ID,
|
||||||
Name: panel.Name,
|
Name: panel.Name,
|
||||||
Info: panel.Info,
|
Info: panel.Info,
|
||||||
Module: panel.Module,
|
Module: panel.Module,
|
||||||
BaseURL: panel.BaseURL,
|
BaseURL: panel.BaseURL,
|
||||||
SkipDataQuery: panel.SkipDataQuery,
|
SkipDataQuery: panel.SkipDataQuery,
|
||||||
HideFromList: panel.HideFromList,
|
HideFromList: panel.HideFromList,
|
||||||
ReleaseState: string(panel.State),
|
ReleaseState: string(panel.State),
|
||||||
Signature: string(panel.Signature),
|
Signature: string(panel.Signature),
|
||||||
Sort: getPanelSort(panel.ID),
|
Sort: getPanelSort(panel.ID),
|
||||||
|
AngularDetected: panel.AngularDetected,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +318,7 @@ func (hs *HTTPServer) getFSDataSources(c *contextmodel.ReqContext, availablePlug
|
|||||||
Module: plugin.Module,
|
Module: plugin.Module,
|
||||||
BaseURL: plugin.BaseURL,
|
BaseURL: plugin.BaseURL,
|
||||||
}
|
}
|
||||||
|
dsDTO.AngularDetected = plugin.AngularDetected
|
||||||
|
|
||||||
if ds.JsonData == nil {
|
if ds.JsonData == nil {
|
||||||
dsDTO.JSONData = make(map[string]interface{})
|
dsDTO.JSONData = make(map[string]interface{})
|
||||||
@ -389,6 +391,7 @@ func (hs *HTTPServer) getFSDataSources(c *contextmodel.ReqContext, availablePlug
|
|||||||
Module: ds.Module,
|
Module: ds.Module,
|
||||||
BaseURL: ds.BaseURL,
|
BaseURL: ds.BaseURL,
|
||||||
},
|
},
|
||||||
|
AngularDetected: ds.AngularDetected,
|
||||||
}
|
}
|
||||||
if ds.Name == grafanads.DatasourceName {
|
if ds.Name == grafanads.DatasourceName {
|
||||||
dto.ID = grafanads.DatasourceID
|
dto.ID = grafanads.DatasourceID
|
||||||
@ -403,10 +406,11 @@ func (hs *HTTPServer) getFSDataSources(c *contextmodel.ReqContext, availablePlug
|
|||||||
|
|
||||||
func newAppDTO(plugin plugins.PluginDTO, settings pluginsettings.InfoDTO) *plugins.AppDTO {
|
func newAppDTO(plugin plugins.PluginDTO, settings pluginsettings.InfoDTO) *plugins.AppDTO {
|
||||||
app := &plugins.AppDTO{
|
app := &plugins.AppDTO{
|
||||||
ID: plugin.ID,
|
ID: plugin.ID,
|
||||||
Version: plugin.Info.Version,
|
Version: plugin.Info.Version,
|
||||||
Path: plugin.Module,
|
Path: plugin.Module,
|
||||||
Preload: false,
|
Preload: false,
|
||||||
|
AngularDetected: plugin.AngularDetected,
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.Enabled {
|
if settings.Enabled {
|
||||||
|
@ -280,6 +280,41 @@ func TestHTTPServer_GetFrontendSettings_apps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "angular app plugin",
|
||||||
|
pluginStore: func() plugins.Store {
|
||||||
|
return &plugins.FakePluginStore{
|
||||||
|
PluginList: []plugins.PluginDTO{
|
||||||
|
{
|
||||||
|
Module: fmt.Sprintf("/%s/module.js", "test-app"),
|
||||||
|
JSONData: plugins.JSONData{
|
||||||
|
ID: "test-app",
|
||||||
|
Info: plugins.Info{Version: "0.5.0"},
|
||||||
|
Type: plugins.App,
|
||||||
|
Preload: true,
|
||||||
|
},
|
||||||
|
AngularDetected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pluginSettings: func() pluginsettings.Service {
|
||||||
|
return &pluginsettings.FakePluginSettings{
|
||||||
|
Plugins: newAppSettings("test-app", true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expected: settings{
|
||||||
|
Apps: map[string]*plugins.AppDTO{
|
||||||
|
"test-app": {
|
||||||
|
ID: "test-app",
|
||||||
|
Preload: true,
|
||||||
|
Path: "/test-app/module.js",
|
||||||
|
Version: "0.5.0",
|
||||||
|
AngularDetected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -128,18 +128,19 @@ func (hs *HTTPServer) GetPluginList(c *contextmodel.ReqContext) response.Respons
|
|||||||
result := make(dtos.PluginList, 0)
|
result := make(dtos.PluginList, 0)
|
||||||
for _, pluginDef := range filteredPluginDefinitions {
|
for _, pluginDef := range filteredPluginDefinitions {
|
||||||
listItem := dtos.PluginListItem{
|
listItem := dtos.PluginListItem{
|
||||||
Id: pluginDef.ID,
|
Id: pluginDef.ID,
|
||||||
Name: pluginDef.Name,
|
Name: pluginDef.Name,
|
||||||
Type: string(pluginDef.Type),
|
Type: string(pluginDef.Type),
|
||||||
Category: pluginDef.Category,
|
Category: pluginDef.Category,
|
||||||
Info: pluginDef.Info,
|
Info: pluginDef.Info,
|
||||||
Dependencies: pluginDef.Dependencies,
|
Dependencies: pluginDef.Dependencies,
|
||||||
DefaultNavUrl: path.Join(hs.Cfg.AppSubURL, pluginDef.DefaultNavURL),
|
DefaultNavUrl: path.Join(hs.Cfg.AppSubURL, pluginDef.DefaultNavURL),
|
||||||
State: pluginDef.State,
|
State: pluginDef.State,
|
||||||
Signature: pluginDef.Signature,
|
Signature: pluginDef.Signature,
|
||||||
SignatureType: pluginDef.SignatureType,
|
SignatureType: pluginDef.SignatureType,
|
||||||
SignatureOrg: pluginDef.SignatureOrg,
|
SignatureOrg: pluginDef.SignatureOrg,
|
||||||
AccessControl: pluginsMetadata[pluginDef.ID],
|
AccessControl: pluginsMetadata[pluginDef.ID],
|
||||||
|
AngularDetected: pluginDef.AngularDetected,
|
||||||
}
|
}
|
||||||
|
|
||||||
update, exists := hs.pluginsUpdateChecker.HasUpdate(c.Req.Context(), pluginDef.ID)
|
update, exists := hs.pluginsUpdateChecker.HasUpdate(c.Req.Context(), pluginDef.ID)
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
package angulardetector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ detector = &containsBytesDetector{}
|
||||||
|
_ detector = ®exDetector{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// detector implements a check to see if a plugin uses Angular.
|
||||||
|
type detector interface {
|
||||||
|
// Detect takes the content of a moduleJs file and returns true if the plugin is using Angular.
|
||||||
|
Detect(moduleJs []byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// containsBytesDetector is a detector that returns true if module.js contains the "pattern" string.
|
||||||
|
type containsBytesDetector struct {
|
||||||
|
pattern []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect returns true if moduleJs contains the byte slice d.pattern.
|
||||||
|
func (d *containsBytesDetector) Detect(moduleJs []byte) bool {
|
||||||
|
return bytes.Contains(moduleJs, d.pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
// regexDetector is a detector that returns true if the module.js content matches a regular expression.
|
||||||
|
type regexDetector struct {
|
||||||
|
regex *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect returns true if moduleJs matches the regular expression d.regex.
|
||||||
|
func (d *regexDetector) Detect(moduleJs []byte) bool {
|
||||||
|
return d.regex.Match(moduleJs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// angularDetectors contains all the detectors to detect Angular plugins.
|
||||||
|
// They are executed in the specified order.
|
||||||
|
var angularDetectors = []detector{
|
||||||
|
&containsBytesDetector{pattern: []byte("PanelCtrl")},
|
||||||
|
&containsBytesDetector{pattern: []byte("QueryCtrl")},
|
||||||
|
&containsBytesDetector{pattern: []byte("app/plugins/sdk")},
|
||||||
|
&containsBytesDetector{pattern: []byte("angular.isNumber(")},
|
||||||
|
&containsBytesDetector{pattern: []byte("editor.html")},
|
||||||
|
&containsBytesDetector{pattern: []byte("ctrl.annotation")},
|
||||||
|
&containsBytesDetector{pattern: []byte("getLegacyAngularInjector")},
|
||||||
|
|
||||||
|
®exDetector{regex: regexp.MustCompile(`['"](app/core/utils/promiseToDigest)|(app/plugins/.*?)|(app/core/core_module)['"]`)},
|
||||||
|
®exDetector{regex: regexp.MustCompile(`from\s+['"]grafana\/app\/`)},
|
||||||
|
®exDetector{regex: regexp.MustCompile(`System\.register\(`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect open module.js and checks if the plugin is using Angular by matching against its source code.
|
||||||
|
// It returns true if module.js matches against any of the detectors in angularDetectors.
|
||||||
|
func Inspect(p *plugins.Plugin) (isAngular bool, err error) {
|
||||||
|
f, err := p.FS.Open("module.js")
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("open module.js: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if closeErr := f.Close(); closeErr != nil && err == nil {
|
||||||
|
err = fmt.Errorf("close module.js: %w", closeErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
b, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("module.js readall: %w", err)
|
||||||
|
}
|
||||||
|
for _, d := range angularDetectors {
|
||||||
|
if d.Detect(b) {
|
||||||
|
isAngular = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package angulardetector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAngularDetector_Inspect(t *testing.T) {
|
||||||
|
type tc struct {
|
||||||
|
name string
|
||||||
|
plugin *plugins.Plugin
|
||||||
|
exp bool
|
||||||
|
}
|
||||||
|
var tcs []tc
|
||||||
|
|
||||||
|
// Angular imports
|
||||||
|
for i, content := range [][]byte{
|
||||||
|
[]byte(`import { MetricsPanelCtrl } from 'grafana/app/plugins/sdk';`),
|
||||||
|
[]byte(`define(["app/plugins/sdk"],(function(n){return function(n){var t={};function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}return e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof`),
|
||||||
|
[]byte(`define(["app/plugins/sdk"],(function(n){return function(n){var t={};function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}return e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toSt`),
|
||||||
|
[]byte(`define(["react","lodash","@grafana/data","@grafana/ui","@emotion/css","@grafana/runtime","moment","app/core/utils/datemath","jquery","app/plugins/sdk","app/core/core_module","app/core/core","app/core/table_model","app/core/utils/kbn","app/core/config","angular"],(function(e,t,r,n,i,a,o,s,u,l,c,p,f,h,d,m){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};retur`),
|
||||||
|
} {
|
||||||
|
tcs = append(tcs, tc{
|
||||||
|
name: "angular " + strconv.Itoa(i),
|
||||||
|
plugin: &plugins.Plugin{
|
||||||
|
FS: plugins.NewInMemoryFS(map[string][]byte{
|
||||||
|
"module.js": content,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
exp: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not angular
|
||||||
|
tcs = append(tcs, tc{
|
||||||
|
name: "not angular",
|
||||||
|
plugin: &plugins.Plugin{
|
||||||
|
FS: plugins.NewInMemoryFS(map[string][]byte{
|
||||||
|
"module.js": []byte(`import { PanelPlugin } from '@grafana/data'`),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
exp: false,
|
||||||
|
})
|
||||||
|
for _, tc := range tcs {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
isAngular, err := Inspect(tc.plugin)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.exp, isAngular)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("no module.js", func(t *testing.T) {
|
||||||
|
p := &plugins.Plugin{FS: plugins.NewInMemoryFS(map[string][]byte{})}
|
||||||
|
_, err := Inspect(p)
|
||||||
|
require.ErrorIs(t, err, plugins.ErrFileNotExist)
|
||||||
|
})
|
||||||
|
}
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/config"
|
"github.com/grafana/grafana/pkg/plugins/config"
|
||||||
"github.com/grafana/grafana/pkg/plugins/log"
|
"github.com/grafana/grafana/pkg/plugins/log"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/loader/angulardetector"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
|
||||||
@ -153,6 +154,15 @@ func (l *Loader) loadPlugins(ctx context.Context, src plugins.PluginSource, foun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect angular for external plugins
|
||||||
|
if plugin.IsExternalPlugin() {
|
||||||
|
var err error
|
||||||
|
plugin.AngularDetected, err = angulardetector.Inspect(plugin)
|
||||||
|
if err != nil {
|
||||||
|
l.log.Warn("could not inspect plugin for angular", "pluginID", plugin.ID, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if plugin.IsApp() {
|
if plugin.IsApp() {
|
||||||
setDefaultNavURL(plugin)
|
setDefaultNavURL(plugin)
|
||||||
}
|
}
|
||||||
|
@ -210,18 +210,19 @@ type PluginMetaDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DataSourceDTO struct {
|
type DataSourceDTO struct {
|
||||||
ID int64 `json:"id,omitempty"`
|
ID int64 `json:"id,omitempty"`
|
||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PluginMeta *PluginMetaDTO `json:"meta"`
|
PluginMeta *PluginMetaDTO `json:"meta"`
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
IsDefault bool `json:"isDefault"`
|
IsDefault bool `json:"isDefault"`
|
||||||
Access string `json:"access,omitempty"`
|
Access string `json:"access,omitempty"`
|
||||||
Preload bool `json:"preload"`
|
Preload bool `json:"preload"`
|
||||||
Module string `json:"module,omitempty"`
|
Module string `json:"module,omitempty"`
|
||||||
JSONData map[string]interface{} `json:"jsonData"`
|
JSONData map[string]interface{} `json:"jsonData"`
|
||||||
ReadOnly bool `json:"readOnly"`
|
ReadOnly bool `json:"readOnly"`
|
||||||
|
AngularDetected bool `json:"angularDetected"`
|
||||||
|
|
||||||
BasicAuth string `json:"basicAuth,omitempty"`
|
BasicAuth string `json:"basicAuth,omitempty"`
|
||||||
WithCredentials bool `json:"withCredentials,omitempty"`
|
WithCredentials bool `json:"withCredentials,omitempty"`
|
||||||
@ -241,23 +242,25 @@ type DataSourceDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PanelDTO struct {
|
type PanelDTO struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Info Info `json:"info"`
|
Info Info `json:"info"`
|
||||||
HideFromList bool `json:"hideFromList"`
|
HideFromList bool `json:"hideFromList"`
|
||||||
Sort int `json:"sort"`
|
Sort int `json:"sort"`
|
||||||
SkipDataQuery bool `json:"skipDataQuery"`
|
SkipDataQuery bool `json:"skipDataQuery"`
|
||||||
ReleaseState string `json:"state"`
|
ReleaseState string `json:"state"`
|
||||||
BaseURL string `json:"baseUrl"`
|
BaseURL string `json:"baseUrl"`
|
||||||
Signature string `json:"signature"`
|
Signature string `json:"signature"`
|
||||||
Module string `json:"module"`
|
Module string `json:"module"`
|
||||||
|
AngularDetected bool `json:"angularDetected"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppDTO struct {
|
type AppDTO struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Preload bool `json:"preload"`
|
Preload bool `json:"preload"`
|
||||||
|
AngularDetected bool `json:"angularDetected"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -51,6 +51,8 @@ type Plugin struct {
|
|||||||
Module string
|
Module string
|
||||||
BaseURL string
|
BaseURL string
|
||||||
|
|
||||||
|
AngularDetected bool
|
||||||
|
|
||||||
Renderer pluginextensionv2.RendererPlugin
|
Renderer pluginextensionv2.RendererPlugin
|
||||||
SecretsManager secretsmanagerplugin.SecretsManagerPlugin
|
SecretsManager secretsmanagerplugin.SecretsManagerPlugin
|
||||||
client backendplugin.Plugin
|
client backendplugin.Plugin
|
||||||
@ -80,6 +82,8 @@ type PluginDTO struct {
|
|||||||
// SystemJS fields
|
// SystemJS fields
|
||||||
Module string
|
Module string
|
||||||
BaseURL string
|
BaseURL string
|
||||||
|
|
||||||
|
AngularDetected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PluginDTO) SupportsStreaming() bool {
|
func (p PluginDTO) SupportsStreaming() bool {
|
||||||
@ -424,6 +428,7 @@ func (p *Plugin) ToDTO() PluginDTO {
|
|||||||
SignatureError: p.SignatureError,
|
SignatureError: p.SignatureError,
|
||||||
Module: p.Module,
|
Module: p.Module,
|
||||||
BaseURL: p.BaseURL,
|
BaseURL: p.BaseURL,
|
||||||
|
AngularDetected: p.AngularDetected,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Alertmanager",
|
"name": "Alertmanager",
|
||||||
@ -74,7 +75,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Annotations list",
|
"name": "Annotations list",
|
||||||
@ -110,7 +112,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Azure Monitor",
|
"name": "Azure Monitor",
|
||||||
@ -168,7 +171,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Bar chart",
|
"name": "Bar chart",
|
||||||
@ -204,7 +208,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Bar gauge",
|
"name": "Bar gauge",
|
||||||
@ -240,7 +245,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Candlestick",
|
"name": "Candlestick",
|
||||||
@ -276,7 +282,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Canvas",
|
"name": "Canvas",
|
||||||
@ -312,7 +319,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CloudWatch",
|
"name": "CloudWatch",
|
||||||
@ -348,7 +356,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Dashboard list",
|
"name": "Dashboard list",
|
||||||
@ -384,7 +393,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Datagrid",
|
"name": "Datagrid",
|
||||||
@ -420,7 +430,8 @@
|
|||||||
"state": "beta",
|
"state": "beta",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Elasticsearch",
|
"name": "Elasticsearch",
|
||||||
@ -461,7 +472,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Flame Graph",
|
"name": "Flame Graph",
|
||||||
@ -497,7 +509,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Gauge",
|
"name": "Gauge",
|
||||||
@ -533,7 +546,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Geomap",
|
"name": "Geomap",
|
||||||
@ -569,7 +583,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Getting Started",
|
"name": "Getting Started",
|
||||||
@ -605,7 +620,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Google Cloud Monitoring",
|
"name": "Google Cloud Monitoring",
|
||||||
@ -641,7 +657,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Grafana Pyroscope",
|
"name": "Grafana Pyroscope",
|
||||||
@ -682,7 +699,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Graph (old)",
|
"name": "Graph (old)",
|
||||||
@ -718,7 +736,8 @@
|
|||||||
"state": "deprecated",
|
"state": "deprecated",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Graphite",
|
"name": "Graphite",
|
||||||
@ -763,7 +782,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Heatmap",
|
"name": "Heatmap",
|
||||||
@ -799,7 +819,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Histogram",
|
"name": "Histogram",
|
||||||
@ -835,7 +856,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "InfluxDB",
|
"name": "InfluxDB",
|
||||||
@ -871,7 +893,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Jaeger",
|
"name": "Jaeger",
|
||||||
@ -916,7 +939,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Logs",
|
"name": "Logs",
|
||||||
@ -952,7 +976,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Loki",
|
"name": "Loki",
|
||||||
@ -997,7 +1022,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Microsoft SQL Server",
|
"name": "Microsoft SQL Server",
|
||||||
@ -1033,7 +1059,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MySQL",
|
"name": "MySQL",
|
||||||
@ -1069,7 +1096,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "News",
|
"name": "News",
|
||||||
@ -1105,7 +1133,8 @@
|
|||||||
"state": "beta",
|
"state": "beta",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Node Graph",
|
"name": "Node Graph",
|
||||||
@ -1141,7 +1170,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenTSDB",
|
"name": "OpenTSDB",
|
||||||
@ -1177,7 +1207,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Parca",
|
"name": "Parca",
|
||||||
@ -1218,7 +1249,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Pie chart",
|
"name": "Pie chart",
|
||||||
@ -1254,7 +1286,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PostgreSQL",
|
"name": "PostgreSQL",
|
||||||
@ -1290,7 +1323,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Prometheus",
|
"name": "Prometheus",
|
||||||
@ -1331,7 +1365,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Stat",
|
"name": "Stat",
|
||||||
@ -1367,7 +1402,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "State timeline",
|
"name": "State timeline",
|
||||||
@ -1403,7 +1439,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Status history",
|
"name": "Status history",
|
||||||
@ -1439,7 +1476,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Table",
|
"name": "Table",
|
||||||
@ -1475,7 +1513,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Table (old)",
|
"name": "Table (old)",
|
||||||
@ -1511,7 +1550,8 @@
|
|||||||
"state": "deprecated",
|
"state": "deprecated",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Tempo",
|
"name": "Tempo",
|
||||||
@ -1552,7 +1592,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "TestData",
|
"name": "TestData",
|
||||||
@ -1588,7 +1629,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Text",
|
"name": "Text",
|
||||||
@ -1624,7 +1666,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Time series",
|
"name": "Time series",
|
||||||
@ -1660,7 +1703,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Traces",
|
"name": "Traces",
|
||||||
@ -1696,7 +1740,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Trend",
|
"name": "Trend",
|
||||||
@ -1732,7 +1777,8 @@
|
|||||||
"state": "beta",
|
"state": "beta",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Welcome",
|
"name": "Welcome",
|
||||||
@ -1768,7 +1814,8 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "XY Chart",
|
"name": "XY Chart",
|
||||||
@ -1804,7 +1851,8 @@
|
|||||||
"state": "beta",
|
"state": "beta",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Zipkin",
|
"name": "Zipkin",
|
||||||
@ -1845,6 +1893,7 @@
|
|||||||
"state": "",
|
"state": "",
|
||||||
"signature": "internal",
|
"signature": "internal",
|
||||||
"signatureType": "",
|
"signatureType": "",
|
||||||
"signatureOrg": ""
|
"signatureOrg": "",
|
||||||
|
"angularDetected": false
|
||||||
}
|
}
|
||||||
]
|
]
|
Loading…
Reference in New Issue
Block a user