mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Add fuzzy search to plugins catalogue (#81001)
* WIP add fuzzysearch to plugins catalog * Add keywords to the plugins listing output * add fuzzy search to plugin catalog, add keywords to plugins at frontend side * refactor fuzzysearch function after review * review changes * change the version of uFuzzy library * change reduce result object in getPluginDetailsForFuzzySearch * fix yarn lock error * fix helpers tests * fix frontend searching test * fix frontend linting issues * fix tests --------- Co-authored-by: Esteban Beltran <esteban@academo.me> Co-authored-by: Giuseppe Guerra <giuseppe@guerra.in>
This commit is contained in:
parent
cf65d91ee9
commit
9dcb7800de
@ -143,6 +143,7 @@ func TestFinder_Find(t *testing.T) {
|
|||||||
{Name: "img1", Path: "img/screenshot1.png"},
|
{Name: "img1", Path: "img/screenshot1.png"},
|
||||||
{Name: "img2", Path: "img/screenshot2.png"},
|
{Name: "img2", Path: "img/screenshot2.png"},
|
||||||
},
|
},
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "3.x.x",
|
GrafanaVersion: "3.x.x",
|
||||||
@ -176,40 +177,19 @@ func TestFinder_Find(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Multiple plugin dirs",
|
name: "Multiple plugin dirs",
|
||||||
pluginDirs: []string{"../../testdata/duplicate-plugins", "../../testdata/invalid-v1-signature"},
|
pluginDirs: []string{"../../testdata/duplicate-plugins", "../../testdata/invalid-v1-signature"},
|
||||||
expectedBundles: []*plugins.FoundBundle{{
|
expectedBundles: []*plugins.FoundBundle{
|
||||||
Primary: plugins.FoundPlugin{
|
{
|
||||||
JSONData: plugins.JSONData{
|
Primary: plugins.FoundPlugin{
|
||||||
ID: "test-app",
|
|
||||||
Type: plugins.TypeDataSource,
|
|
||||||
Name: "Parent",
|
|
||||||
Info: plugins.Info{
|
|
||||||
Author: plugins.InfoLink{
|
|
||||||
Name: "Grafana Labs",
|
|
||||||
URL: "http://grafana.com",
|
|
||||||
},
|
|
||||||
Description: "Parent plugin",
|
|
||||||
Version: "1.0.0",
|
|
||||||
Updated: "2020-10-20",
|
|
||||||
},
|
|
||||||
Dependencies: plugins.Dependencies{
|
|
||||||
GrafanaVersion: "*",
|
|
||||||
Plugins: []plugins.Dependency{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
FS: mustNewStaticFSForTests(t, filepath.Join(testData, "duplicate-plugins/nested")),
|
|
||||||
},
|
|
||||||
Children: []*plugins.FoundPlugin{
|
|
||||||
{
|
|
||||||
JSONData: plugins.JSONData{
|
JSONData: plugins.JSONData{
|
||||||
ID: "test-app",
|
ID: "test-app",
|
||||||
Type: plugins.TypeDataSource,
|
Type: plugins.TypeDataSource,
|
||||||
Name: "Child",
|
Name: "Parent",
|
||||||
Info: plugins.Info{
|
Info: plugins.Info{
|
||||||
Author: plugins.InfoLink{
|
Author: plugins.InfoLink{
|
||||||
Name: "Grafana Labs",
|
Name: "Grafana Labs",
|
||||||
URL: "http://grafana.com",
|
URL: "http://grafana.com",
|
||||||
},
|
},
|
||||||
Description: "Child plugin",
|
Description: "Parent plugin",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
Updated: "2020-10-20",
|
Updated: "2020-10-20",
|
||||||
},
|
},
|
||||||
@ -218,10 +198,32 @@ func TestFinder_Find(t *testing.T) {
|
|||||||
Plugins: []plugins.Dependency{},
|
Plugins: []plugins.Dependency{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FS: mustNewStaticFSForTests(t, filepath.Join(testData, "duplicate-plugins/nested/nested")),
|
FS: mustNewStaticFSForTests(t, filepath.Join(testData, "duplicate-plugins/nested")),
|
||||||
|
},
|
||||||
|
Children: []*plugins.FoundPlugin{
|
||||||
|
{
|
||||||
|
JSONData: plugins.JSONData{
|
||||||
|
ID: "test-app",
|
||||||
|
Type: plugins.TypeDataSource,
|
||||||
|
Name: "Child",
|
||||||
|
Info: plugins.Info{
|
||||||
|
Author: plugins.InfoLink{
|
||||||
|
Name: "Grafana Labs",
|
||||||
|
URL: "http://grafana.com",
|
||||||
|
},
|
||||||
|
Description: "Child plugin",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Updated: "2020-10-20",
|
||||||
|
},
|
||||||
|
Dependencies: plugins.Dependencies{
|
||||||
|
GrafanaVersion: "*",
|
||||||
|
Plugins: []plugins.Dependency{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FS: mustNewStaticFSForTests(t, filepath.Join(testData, "duplicate-plugins/nested/nested")),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Primary: plugins.FoundPlugin{
|
Primary: plugins.FoundPlugin{
|
||||||
JSONData: plugins.JSONData{
|
JSONData: plugins.JSONData{
|
||||||
|
@ -154,7 +154,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
Class: plugins.ClassBundled,
|
Class: plugins.ClassBundled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: "Load plugin with symbolic links",
|
name: "Load plugin with symbolic links",
|
||||||
class: plugins.ClassExternal,
|
class: plugins.ClassExternal,
|
||||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||||
@ -183,8 +184,9 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
{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/screenshot2.png", Name: "img2"},
|
||||||
},
|
},
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "3.x.x",
|
GrafanaVersion: "3.x.x",
|
||||||
@ -212,7 +214,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
Name: "Nginx Panel",
|
Name: "Nginx Panel",
|
||||||
Type: string(plugins.TypePanel),
|
Type: string(plugins.TypePanel),
|
||||||
Role: org.RoleViewer,
|
Role: org.RoleViewer,
|
||||||
Slug: "nginx-panel"},
|
Slug: "nginx-panel",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "Nginx Datasource",
|
Name: "Nginx Datasource",
|
||||||
Type: string(plugins.TypeDataSource),
|
Type: string(plugins.TypeDataSource),
|
||||||
@ -230,7 +233,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
SignatureOrg: "Grafana Labs",
|
SignatureOrg: "Grafana Labs",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: "Load an unsigned plugin (development)",
|
name: "Load an unsigned plugin (development)",
|
||||||
class: plugins.ClassExternal,
|
class: plugins.ClassExternal,
|
||||||
cfg: &config.Cfg{
|
cfg: &config.Cfg{
|
||||||
@ -383,7 +387,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
Small: "public/img/icn-app.svg",
|
Small: "public/img/icn-app.svg",
|
||||||
Large: "public/img/icn-app.svg",
|
Large: "public/img/icn-app.svg",
|
||||||
},
|
},
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaDependency: ">=8.0.0",
|
GrafanaDependency: ">=8.0.0",
|
||||||
|
@ -130,6 +130,7 @@ type Info struct {
|
|||||||
Screenshots []Screenshots `json:"screenshots"`
|
Screenshots []Screenshots `json:"screenshots"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Updated string `json:"updated"`
|
Updated string `json:"updated"`
|
||||||
|
Keywords []string `json:"keywords"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InfoLink struct {
|
type InfoLink struct {
|
||||||
|
@ -49,7 +49,8 @@ func Test_ReadPluginJSON(t *testing.T) {
|
|||||||
{Path: "img/screenshot1.png", Name: "img1"},
|
{Path: "img/screenshot1.png", Name: "img1"},
|
||||||
{Path: "img/screenshot2.png", Name: "img2"},
|
{Path: "img/screenshot2.png", Name: "img2"},
|
||||||
},
|
},
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: Dependencies{
|
Dependencies: Dependencies{
|
||||||
GrafanaVersion: "3.x.x",
|
GrafanaVersion: "3.x.x",
|
||||||
@ -107,7 +108,7 @@ func Test_ReadPluginJSON(t *testing.T) {
|
|||||||
pluginJSON: func(t *testing.T) io.ReadCloser {
|
pluginJSON: func(t *testing.T) io.ReadCloser {
|
||||||
pJSON := `{
|
pJSON := `{
|
||||||
"id": "grafana-pyroscope-datasource",
|
"id": "grafana-pyroscope-datasource",
|
||||||
"type": "datasource",
|
"type": "datasource",
|
||||||
"aliasIDs": ["phlare"]
|
"aliasIDs": ["phlare"]
|
||||||
}`
|
}`
|
||||||
return io.NopCloser(strings.NewReader(pJSON))
|
return io.NopCloser(strings.NewReader(pJSON))
|
||||||
|
@ -154,7 +154,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
Class: plugins.ClassBundled,
|
Class: plugins.ClassBundled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: "Load plugin with symbolic links",
|
name: "Load plugin with symbolic links",
|
||||||
class: plugins.ClassExternal,
|
class: plugins.ClassExternal,
|
||||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||||
@ -183,8 +184,9 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
{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/screenshot2.png", Name: "img2"},
|
||||||
},
|
},
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "3.x.x",
|
GrafanaVersion: "3.x.x",
|
||||||
@ -212,7 +214,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
Name: "Nginx Panel",
|
Name: "Nginx Panel",
|
||||||
Type: string(plugins.TypePanel),
|
Type: string(plugins.TypePanel),
|
||||||
Role: org.RoleViewer,
|
Role: org.RoleViewer,
|
||||||
Slug: "nginx-panel"},
|
Slug: "nginx-panel",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "Nginx Datasource",
|
Name: "Nginx Datasource",
|
||||||
Type: string(plugins.TypeDataSource),
|
Type: string(plugins.TypeDataSource),
|
||||||
@ -230,7 +233,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
SignatureOrg: "Grafana Labs",
|
SignatureOrg: "Grafana Labs",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: "Load an unsigned plugin (development)",
|
name: "Load an unsigned plugin (development)",
|
||||||
class: plugins.ClassExternal,
|
class: plugins.ClassExternal,
|
||||||
cfg: &config.Cfg{
|
cfg: &config.Cfg{
|
||||||
@ -269,7 +273,8 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
Signature: "unsigned",
|
Signature: "unsigned",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: "Load an unsigned plugin (production)",
|
name: "Load an unsigned plugin (production)",
|
||||||
class: plugins.ClassExternal,
|
class: plugins.ClassExternal,
|
||||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||||
@ -392,38 +397,40 @@ func TestLoader_Load(t *testing.T) {
|
|||||||
},
|
},
|
||||||
pluginPaths: []string{filepath.Join(testDataDir(t), "test-app-with-includes")},
|
pluginPaths: []string{filepath.Join(testDataDir(t), "test-app-with-includes")},
|
||||||
want: []*plugins.Plugin{
|
want: []*plugins.Plugin{
|
||||||
{JSONData: plugins.JSONData{
|
{
|
||||||
ID: "test-app",
|
JSONData: plugins.JSONData{
|
||||||
Type: plugins.TypeApp,
|
ID: "test-app",
|
||||||
Name: "Test App",
|
Type: plugins.TypeApp,
|
||||||
Info: plugins.Info{
|
Name: "Test App",
|
||||||
Author: plugins.InfoLink{
|
Info: plugins.Info{
|
||||||
Name: "Test Inc.",
|
Author: plugins.InfoLink{
|
||||||
URL: "http://test.com",
|
Name: "Test Inc.",
|
||||||
|
URL: "http://test.com",
|
||||||
|
},
|
||||||
|
Description: "Official Grafana Test App & Dashboard bundle",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Links: []plugins.InfoLink{
|
||||||
|
{Name: "Project site", URL: "http://project.com"},
|
||||||
|
{Name: "License & Terms", URL: "http://license.com"},
|
||||||
|
},
|
||||||
|
Logos: plugins.Logos{
|
||||||
|
Small: "public/img/icn-app.svg",
|
||||||
|
Large: "public/img/icn-app.svg",
|
||||||
|
},
|
||||||
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Description: "Official Grafana Test App & Dashboard bundle",
|
Dependencies: plugins.Dependencies{
|
||||||
Version: "1.0.0",
|
GrafanaDependency: ">=8.0.0",
|
||||||
Links: []plugins.InfoLink{
|
GrafanaVersion: "*",
|
||||||
{Name: "Project site", URL: "http://project.com"},
|
Plugins: []plugins.Dependency{},
|
||||||
{Name: "License & Terms", URL: "http://license.com"},
|
|
||||||
},
|
},
|
||||||
Logos: plugins.Logos{
|
Includes: []*plugins.Includes{
|
||||||
Small: "public/img/icn-app.svg",
|
{Name: "Nginx Memory", Path: "dashboards/memory.json", Type: "dashboard", Role: org.RoleViewer, Slug: "nginx-memory"},
|
||||||
Large: "public/img/icn-app.svg",
|
{Name: "Root Page (react)", Type: "page", Role: org.RoleViewer, Path: "/a/my-simple-app", DefaultNav: true, AddToNav: true, Slug: "root-page-react"},
|
||||||
},
|
},
|
||||||
Updated: "2015-02-10",
|
Backend: false,
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
|
||||||
GrafanaDependency: ">=8.0.0",
|
|
||||||
GrafanaVersion: "*",
|
|
||||||
Plugins: []plugins.Dependency{},
|
|
||||||
},
|
|
||||||
Includes: []*plugins.Includes{
|
|
||||||
{Name: "Nginx Memory", Path: "dashboards/memory.json", Type: "dashboard", Role: org.RoleViewer, Slug: "nginx-memory"},
|
|
||||||
{Name: "Root Page (react)", Type: "page", Role: org.RoleViewer, Path: "/a/my-simple-app", DefaultNav: true, AddToNav: true, Slug: "root-page-react"},
|
|
||||||
},
|
|
||||||
Backend: false,
|
|
||||||
},
|
|
||||||
DefaultNavURL: "/plugins/test-app/page/root-page-react",
|
DefaultNavURL: "/plugins/test-app/page/root-page-react",
|
||||||
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "test-app-with-includes")),
|
FS: mustNewStaticFSForTests(t, filepath.Join(testDataDir(t), "test-app-with-includes")),
|
||||||
Class: plugins.ClassExternal,
|
Class: plugins.ClassExternal,
|
||||||
@ -570,10 +577,12 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
|||||||
backendFactoryProvider.BackendFactoryFunc = func(ctx context.Context, plugin *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
backendFactoryProvider.BackendFactoryFunc = func(ctx context.Context, plugin *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||||
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
|
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
|
||||||
require.Equal(t, "grafana-test-datasource", pluginID)
|
require.Equal(t, "grafana-test-datasource", pluginID)
|
||||||
require.Equal(t, []string{"GF_VERSION=", "GF_EDITION=", "GF_ENTERPRISE_LICENSE_PATH=",
|
require.Equal(t, []string{
|
||||||
|
"GF_VERSION=", "GF_EDITION=", "GF_ENTERPRISE_LICENSE_PATH=",
|
||||||
"GF_ENTERPRISE_APP_URL=", "GF_ENTERPRISE_LICENSE_TEXT=", "GF_APP_URL=",
|
"GF_ENTERPRISE_APP_URL=", "GF_ENTERPRISE_LICENSE_TEXT=", "GF_APP_URL=",
|
||||||
"GF_PLUGIN_APP_CLIENT_ID=client-id", "GF_PLUGIN_APP_CLIENT_SECRET=secretz",
|
"GF_PLUGIN_APP_CLIENT_ID=client-id", "GF_PLUGIN_APP_CLIENT_SECRET=secretz",
|
||||||
"GF_PLUGIN_APP_PRIVATE_KEY=priv@t3", "GF_INSTANCE_FEATURE_TOGGLES_ENABLE=externalServiceAuth"}, env())
|
"GF_PLUGIN_APP_PRIVATE_KEY=priv@t3", "GF_INSTANCE_FEATURE_TOGGLES_ENABLE=externalServiceAuth",
|
||||||
|
}, env())
|
||||||
return &fakes.FakeBackendPlugin{}, nil
|
return &fakes.FakeBackendPlugin{}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -614,38 +623,39 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
pluginPaths := []string{filepath.Join(testDataDir(t), "external-registration")}
|
pluginPaths := []string{filepath.Join(testDataDir(t), "external-registration")}
|
||||||
expected := []*plugins.Plugin{
|
expected := []*plugins.Plugin{
|
||||||
{JSONData: plugins.JSONData{
|
{
|
||||||
ID: "grafana-test-datasource",
|
JSONData: plugins.JSONData{
|
||||||
Type: plugins.TypeDataSource,
|
ID: "grafana-test-datasource",
|
||||||
Name: "Test",
|
Type: plugins.TypeDataSource,
|
||||||
Backend: true,
|
Name: "Test",
|
||||||
Executable: "gpx_test_datasource",
|
Backend: true,
|
||||||
Info: plugins.Info{
|
Executable: "gpx_test_datasource",
|
||||||
Author: plugins.InfoLink{
|
Info: plugins.Info{
|
||||||
Name: "Grafana Labs",
|
Author: plugins.InfoLink{
|
||||||
URL: "https://grafana.com",
|
Name: "Grafana Labs",
|
||||||
|
URL: "https://grafana.com",
|
||||||
|
},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
Updated: "2023-08-03",
|
||||||
|
Screenshots: []plugins.Screenshots{},
|
||||||
},
|
},
|
||||||
Version: "1.0.0",
|
Dependencies: plugins.Dependencies{
|
||||||
Logos: plugins.Logos{
|
GrafanaVersion: "*",
|
||||||
Small: "public/plugins/grafana-test-datasource/img/ds.svg",
|
Plugins: []plugins.Dependency{},
|
||||||
Large: "public/plugins/grafana-test-datasource/img/ds.svg",
|
|
||||||
},
|
},
|
||||||
Updated: "2023-08-03",
|
IAM: &plugindef.IAM{
|
||||||
Screenshots: []plugins.Screenshots{},
|
Permissions: []plugindef.Permission{
|
||||||
},
|
{
|
||||||
Dependencies: plugins.Dependencies{
|
Action: "read",
|
||||||
GrafanaVersion: "*",
|
Scope: stringPtr("datasource"),
|
||||||
Plugins: []plugins.Dependency{},
|
},
|
||||||
},
|
|
||||||
IAM: &plugindef.IAM{
|
|
||||||
Permissions: []plugindef.Permission{
|
|
||||||
{
|
|
||||||
Action: "read",
|
|
||||||
Scope: stringPtr("datasource"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
FS: mustNewStaticFSForTests(t, pluginPaths[0]),
|
FS: mustNewStaticFSForTests(t, pluginPaths[0]),
|
||||||
Class: plugins.ClassExternal,
|
Class: plugins.ClassExternal,
|
||||||
Signature: plugins.SignatureStatusUnsigned,
|
Signature: plugins.SignatureStatusUnsigned,
|
||||||
@ -662,10 +672,12 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
|||||||
backendFactoryProvider.BackendFactoryFunc = func(ctx context.Context, plugin *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
backendFactoryProvider.BackendFactoryFunc = func(ctx context.Context, plugin *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||||
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
|
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
|
||||||
require.Equal(t, "grafana-test-datasource", pluginID)
|
require.Equal(t, "grafana-test-datasource", pluginID)
|
||||||
require.Equal(t, []string{"GF_VERSION=", "GF_EDITION=", "GF_ENTERPRISE_LICENSE_PATH=",
|
require.Equal(t, []string{
|
||||||
|
"GF_VERSION=", "GF_EDITION=", "GF_ENTERPRISE_LICENSE_PATH=",
|
||||||
"GF_ENTERPRISE_APP_URL=", "GF_ENTERPRISE_LICENSE_TEXT=", "GF_APP_URL=",
|
"GF_ENTERPRISE_APP_URL=", "GF_ENTERPRISE_LICENSE_TEXT=", "GF_APP_URL=",
|
||||||
"GF_PLUGIN_APP_CLIENT_ID=client-id", "GF_PLUGIN_APP_CLIENT_SECRET=secretz",
|
"GF_PLUGIN_APP_CLIENT_ID=client-id", "GF_PLUGIN_APP_CLIENT_SECRET=secretz",
|
||||||
"GF_INSTANCE_FEATURE_TOGGLES_ENABLE=externalServiceAuth"}, env())
|
"GF_INSTANCE_FEATURE_TOGGLES_ENABLE=externalServiceAuth",
|
||||||
|
}, env())
|
||||||
return &fakes.FakeBackendPlugin{}, nil
|
return &fakes.FakeBackendPlugin{}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -906,7 +918,8 @@ func TestLoader_Load_RBACReady(t *testing.T) {
|
|||||||
Small: "public/img/icn-app.svg",
|
Small: "public/img/icn-app.svg",
|
||||||
Large: "public/img/icn-app.svg",
|
Large: "public/img/icn-app.svg",
|
||||||
},
|
},
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "*",
|
GrafanaVersion: "*",
|
||||||
@ -1049,7 +1062,8 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
|
|||||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
{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/screenshot2.png", Name: "img2"},
|
||||||
},
|
},
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "3.x.x",
|
GrafanaVersion: "3.x.x",
|
||||||
@ -1129,7 +1143,8 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
|
|||||||
{Path: "public/plugins/test-app/img/screenshot1.png", Name: "img1"},
|
{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/screenshot2.png", Name: "img2"},
|
||||||
},
|
},
|
||||||
Updated: "2015-02-10",
|
Updated: "2015-02-10",
|
||||||
|
Keywords: []string{"test"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "3.x.x",
|
GrafanaVersion: "3.x.x",
|
||||||
@ -1476,6 +1491,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
|||||||
Description: "Grafana App Plugin Template",
|
Description: "Grafana App Plugin Template",
|
||||||
Version: "",
|
Version: "",
|
||||||
Updated: "",
|
Updated: "",
|
||||||
|
Keywords: []string{"panel", "template"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaVersion: "7.0.0",
|
GrafanaVersion: "7.0.0",
|
||||||
@ -1556,6 +1572,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
|||||||
Description: "Grafana Panel Plugin Template",
|
Description: "Grafana Panel Plugin Template",
|
||||||
Version: "",
|
Version: "",
|
||||||
Updated: "",
|
Updated: "",
|
||||||
|
Keywords: []string{"panel", "template"},
|
||||||
},
|
},
|
||||||
Dependencies: plugins.Dependencies{
|
Dependencies: plugins.Dependencies{
|
||||||
GrafanaDependency: ">=7.0.0",
|
GrafanaDependency: ">=7.0.0",
|
||||||
@ -1612,7 +1629,8 @@ type loaderDepOpts struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process.Manager,
|
func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process.Manager,
|
||||||
backendFactory plugins.BackendFactoryProvider, sigErrTracker pluginerrs.SignatureErrorTracker) *Loader {
|
backendFactory plugins.BackendFactoryProvider, sigErrTracker pluginerrs.SignatureErrorTracker,
|
||||||
|
) *Loader {
|
||||||
assets := assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
assets := assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||||
lic := fakes.NewFakeLicensingService()
|
lic := fakes.NewFakeLicensingService()
|
||||||
angularInspector := angularinspector.NewStaticInspector()
|
angularInspector := angularinspector.NewStaticInspector()
|
||||||
@ -1662,7 +1680,8 @@ func newLoaderWithOpts(t *testing.T, cfg *config.Cfg, opts loaderDepOpts) *Loade
|
|||||||
}
|
}
|
||||||
|
|
||||||
func verifyState(t *testing.T, ps []*plugins.Plugin, reg registry.Service,
|
func verifyState(t *testing.T, ps []*plugins.Plugin, reg registry.Service,
|
||||||
procPrvdr *fakes.FakeBackendProcessProvider, procMngr *fakes.FakeProcessManager) {
|
procPrvdr *fakes.FakeBackendProcessProvider, procMngr *fakes.FakeProcessManager,
|
||||||
|
) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -61,7 +62,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -98,7 +100,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -157,7 +160,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": [
|
||||||
|
"azure",
|
||||||
|
"monitor",
|
||||||
|
"Application Insights",
|
||||||
|
"Log Analytics",
|
||||||
|
"App Insights"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "\u003e=10.3.0",
|
"grafanaDependency": "\u003e=10.3.0",
|
||||||
@ -194,7 +204,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -231,7 +242,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -268,7 +280,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -305,7 +318,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -342,7 +356,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -379,7 +394,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -416,7 +432,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -458,7 +475,10 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": [
|
||||||
|
"elasticsearch"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -495,7 +515,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -532,7 +553,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -569,7 +591,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -606,7 +629,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -643,7 +667,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -685,7 +710,16 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": [
|
||||||
|
"grafana",
|
||||||
|
"datasource",
|
||||||
|
"phlare",
|
||||||
|
"flamegraph",
|
||||||
|
"profiling",
|
||||||
|
"continuous profiling",
|
||||||
|
"pyroscope"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "\u003e=10.3.0-0",
|
"grafanaDependency": "\u003e=10.3.0-0",
|
||||||
@ -722,7 +756,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -768,7 +803,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -805,7 +841,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -842,7 +879,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -879,7 +917,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -925,7 +964,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -962,7 +1002,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1008,7 +1049,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1045,7 +1087,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1082,7 +1125,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1119,7 +1163,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1156,7 +1201,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1193,7 +1239,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1235,7 +1282,13 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": [
|
||||||
|
"grafana",
|
||||||
|
"datasource",
|
||||||
|
"parca",
|
||||||
|
"profiling"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "\u003e=10.3.0-0",
|
"grafanaDependency": "\u003e=10.3.0-0",
|
||||||
@ -1272,7 +1325,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1309,7 +1363,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1351,7 +1406,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1388,7 +1444,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1425,7 +1482,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1462,7 +1520,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1499,7 +1558,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1536,7 +1596,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1578,7 +1639,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "\u003e=10.3.0-0",
|
"grafanaDependency": "\u003e=10.3.0-0",
|
||||||
@ -1615,7 +1677,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "\u003e=10.3.0-0",
|
"grafanaDependency": "\u003e=10.3.0-0",
|
||||||
@ -1652,7 +1715,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1689,7 +1753,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1726,7 +1791,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1763,7 +1829,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1800,7 +1867,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1837,7 +1905,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
@ -1879,7 +1948,8 @@
|
|||||||
"build": {},
|
"build": {},
|
||||||
"screenshots": null,
|
"screenshots": null,
|
||||||
"version": "",
|
"version": "",
|
||||||
"updated": ""
|
"updated": "",
|
||||||
|
"keywords": null
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"grafanaDependency": "",
|
"grafanaDependency": "",
|
||||||
|
@ -11,6 +11,7 @@ export default {
|
|||||||
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.2.2/logos/small',
|
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.2.2/logos/small',
|
||||||
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.2.2/logos/large',
|
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.2.2/logos/large',
|
||||||
},
|
},
|
||||||
|
keywords: ['zabbix', 'monitoring', 'dashboard'],
|
||||||
},
|
},
|
||||||
isCore: false,
|
isCore: false,
|
||||||
isDev: false,
|
isDev: false,
|
||||||
|
@ -9,6 +9,7 @@ export default {
|
|||||||
downloads: 33645089,
|
downloads: 33645089,
|
||||||
featured: 180,
|
featured: 180,
|
||||||
id: 74,
|
id: 74,
|
||||||
|
keywords: ['zabbix', 'monitoring', 'dashboard'],
|
||||||
typeId: 1,
|
typeId: 1,
|
||||||
typeName: 'Application',
|
typeName: 'Application',
|
||||||
internal: false,
|
internal: false,
|
||||||
|
@ -17,6 +17,7 @@ const plugin: CatalogPlugin = {
|
|||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
info: {
|
info: {
|
||||||
logos: { small: '', large: '' },
|
logos: { small: '', large: '' },
|
||||||
|
keywords: ['test', 'plugin'],
|
||||||
},
|
},
|
||||||
name: 'Testing Plugin',
|
name: 'Testing Plugin',
|
||||||
orgName: 'Test',
|
orgName: 'Test',
|
||||||
|
@ -15,6 +15,7 @@ const plugin: CatalogPlugin = {
|
|||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
info: {
|
info: {
|
||||||
logos: { small: '', large: '' },
|
logos: { small: '', large: '' },
|
||||||
|
keywords: ['test', 'plugin'],
|
||||||
},
|
},
|
||||||
name: 'Testing Plugin',
|
name: 'Testing Plugin',
|
||||||
orgName: 'Test',
|
orgName: 'Test',
|
||||||
|
@ -32,6 +32,7 @@ const getMockPlugin = (id: string): CatalogPlugin => {
|
|||||||
small: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/small',
|
small: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/small',
|
||||||
large: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/large',
|
large: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/large',
|
||||||
},
|
},
|
||||||
|
keywords: ['test', 'plugin'],
|
||||||
},
|
},
|
||||||
name: 'Testing Plugin',
|
name: 'Testing Plugin',
|
||||||
orgName: 'Test',
|
orgName: 'Test',
|
||||||
|
@ -42,6 +42,7 @@ describe('PluginListItem', () => {
|
|||||||
small: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/small',
|
small: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/small',
|
||||||
large: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/large',
|
large: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/large',
|
||||||
},
|
},
|
||||||
|
keywords: ['test', 'plugin'],
|
||||||
},
|
},
|
||||||
name: 'Testing Plugin',
|
name: 'Testing Plugin',
|
||||||
orgName: 'Test',
|
orgName: 'Test',
|
||||||
|
@ -18,6 +18,7 @@ describe('PluginListItemBadges', () => {
|
|||||||
small: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/small',
|
small: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/small',
|
||||||
large: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/large',
|
large: 'https://grafana.com/api/plugins/test-plugin/versions/0.0.10/logos/large',
|
||||||
},
|
},
|
||||||
|
keywords: ['test', 'plugin'],
|
||||||
},
|
},
|
||||||
name: 'Testing Plugin',
|
name: 'Testing Plugin',
|
||||||
orgName: 'Test',
|
orgName: 'Test',
|
||||||
|
@ -140,6 +140,7 @@ describe('Plugins/Helpers', () => {
|
|||||||
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/large',
|
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/large',
|
||||||
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/small',
|
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/small',
|
||||||
},
|
},
|
||||||
|
keywords: ['zabbix', 'monitoring', 'dashboard'],
|
||||||
},
|
},
|
||||||
error: undefined,
|
error: undefined,
|
||||||
isCore: false,
|
isCore: false,
|
||||||
@ -266,6 +267,7 @@ describe('Plugins/Helpers', () => {
|
|||||||
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/small',
|
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/small',
|
||||||
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/large',
|
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/large',
|
||||||
},
|
},
|
||||||
|
keywords: ['zabbix', 'monitoring', 'dashboard'],
|
||||||
},
|
},
|
||||||
error: undefined,
|
error: undefined,
|
||||||
isCore: false,
|
isCore: false,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import uFuzzy from '@leeoniya/ufuzzy';
|
||||||
|
|
||||||
import { PluginSignatureStatus, dateTimeParse, PluginError, PluginType, PluginErrorCode } from '@grafana/data';
|
import { PluginSignatureStatus, dateTimeParse, PluginError, PluginType, PluginErrorCode } from '@grafana/data';
|
||||||
import { config, featureEnabled } from '@grafana/runtime';
|
import { config, featureEnabled } from '@grafana/runtime';
|
||||||
import configCore, { Settings } from 'app/core/config';
|
import configCore, { Settings } from 'app/core/config';
|
||||||
@ -86,6 +88,7 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C
|
|||||||
createdAt: publishedAt,
|
createdAt: publishedAt,
|
||||||
status,
|
status,
|
||||||
angularDetected,
|
angularDetected,
|
||||||
|
keywords,
|
||||||
} = plugin;
|
} = plugin;
|
||||||
|
|
||||||
const isDisabled = !!error || isDisabledSecretsPlugin(typeCode);
|
const isDisabled = !!error || isDisabledSecretsPlugin(typeCode);
|
||||||
@ -98,6 +101,7 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C
|
|||||||
small: `https://grafana.com/api/plugins/${id}/versions/${version}/logos/small`,
|
small: `https://grafana.com/api/plugins/${id}/versions/${version}/logos/small`,
|
||||||
large: `https://grafana.com/api/plugins/${id}/versions/${version}/logos/large`,
|
large: `https://grafana.com/api/plugins/${id}/versions/${version}/logos/large`,
|
||||||
},
|
},
|
||||||
|
keywords,
|
||||||
},
|
},
|
||||||
name,
|
name,
|
||||||
orgName,
|
orgName,
|
||||||
@ -123,7 +127,7 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C
|
|||||||
export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): CatalogPlugin {
|
export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): CatalogPlugin {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
info: { description, version, logos, updated, author },
|
info: { description, version, logos, updated, author, keywords },
|
||||||
id,
|
id,
|
||||||
dev,
|
dev,
|
||||||
type,
|
type,
|
||||||
@ -140,7 +144,7 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat
|
|||||||
description,
|
description,
|
||||||
downloads: 0,
|
downloads: 0,
|
||||||
id,
|
id,
|
||||||
info: { logos },
|
info: { logos, keywords },
|
||||||
name,
|
name,
|
||||||
orgName: author.name,
|
orgName: author.name,
|
||||||
popularity: 0,
|
popularity: 0,
|
||||||
@ -173,6 +177,7 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e
|
|||||||
const id = remote?.slug || local?.id || '';
|
const id = remote?.slug || local?.id || '';
|
||||||
const type = local?.type || remote?.typeCode;
|
const type = local?.type || remote?.typeCode;
|
||||||
const isDisabled = !!error || isDisabledSecretsPlugin(type);
|
const isDisabled = !!error || isDisabledSecretsPlugin(type);
|
||||||
|
const keywords = remote?.keywords || local?.info.keywords || [];
|
||||||
|
|
||||||
let logos = {
|
let logos = {
|
||||||
small: `/public/img/icn-${type}.svg`,
|
small: `/public/img/icn-${type}.svg`,
|
||||||
@ -195,6 +200,7 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e
|
|||||||
id,
|
id,
|
||||||
info: {
|
info: {
|
||||||
logos,
|
logos,
|
||||||
|
keywords,
|
||||||
},
|
},
|
||||||
isCore: Boolean(remote?.internal || local?.signature === PluginSignatureStatus.internal),
|
isCore: Boolean(remote?.internal || local?.signature === PluginSignatureStatus.internal),
|
||||||
isDev: Boolean(local?.dev),
|
isDev: Boolean(local?.dev),
|
||||||
@ -345,3 +351,26 @@ function isDisabledSecretsPlugin(type?: PluginType): boolean {
|
|||||||
export function isLocalCorePlugin(local?: LocalPlugin): boolean {
|
export function isLocalCorePlugin(local?: LocalPlugin): boolean {
|
||||||
return Boolean(local?.signature === 'internal');
|
return Boolean(local?.signature === 'internal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getId(inputString: string): string {
|
||||||
|
const parts = inputString.split(' - ');
|
||||||
|
return parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginDetailsForFuzzySearch(plugins: CatalogPlugin[]): string[] {
|
||||||
|
return plugins.reduce((result: string[], { id, name, type, orgName, info }: CatalogPlugin) => {
|
||||||
|
const keywordsForSearch = info.keywords?.join(' ').toLowerCase();
|
||||||
|
const pluginString = `${id} - ${name} - ${type} - ${orgName} - ${keywordsForSearch}`;
|
||||||
|
result.push(pluginString);
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
export function filterByKeyword(plugins: CatalogPlugin[], query: string) {
|
||||||
|
const dataArray = getPluginDetailsForFuzzySearch(plugins);
|
||||||
|
let uf = new uFuzzy({});
|
||||||
|
let idxs = uf.filter(dataArray, query);
|
||||||
|
if (idxs === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return idxs.map((id) => getId(dataArray[id]));
|
||||||
|
}
|
||||||
|
@ -183,13 +183,19 @@ describe('Browse list of plugins', () => {
|
|||||||
|
|
||||||
describe('when searching', () => {
|
describe('when searching', () => {
|
||||||
it('should only list plugins matching search', async () => {
|
it('should only list plugins matching search', async () => {
|
||||||
const { queryByText } = renderBrowse('/plugins?filterBy=all&q=zabbix', [
|
const { queryByText } = renderBrowse('/plugins?filterBy=all&q=matches', [
|
||||||
getCatalogPluginMock({ id: 'zabbix', name: 'Zabbix' }),
|
getCatalogPluginMock({ id: 'matches-the-search', name: 'Matches the search' }),
|
||||||
getCatalogPluginMock({ id: 'plugin-2', name: 'Plugin 2' }),
|
getCatalogPluginMock({
|
||||||
getCatalogPluginMock({ id: 'plugin-3', name: 'Plugin 3' }),
|
id: 'plugin-2',
|
||||||
|
name: 'Plugin 2',
|
||||||
|
}),
|
||||||
|
getCatalogPluginMock({
|
||||||
|
id: 'plugin-3',
|
||||||
|
name: 'Plugin 3',
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await waitFor(() => expect(queryByText('Zabbix')).toBeInTheDocument());
|
await waitFor(() => expect(queryByText('Matches the search')).toBeInTheDocument());
|
||||||
|
|
||||||
// Other plugin types shouldn't be shown
|
// Other plugin types shouldn't be shown
|
||||||
expect(queryByText('Plugin 2')).not.toBeInTheDocument();
|
expect(queryByText('Plugin 2')).not.toBeInTheDocument();
|
||||||
|
@ -39,6 +39,7 @@ export default function Browse({ route }: GrafanaRouteComponentProps): ReactElem
|
|||||||
},
|
},
|
||||||
sortBy
|
sortBy
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterByOptions = [
|
const filterByOptions = [
|
||||||
{ value: 'all', label: 'All' },
|
{ value: 'all', label: 'All' },
|
||||||
{ value: 'installed', label: 'Installed' },
|
{ value: 'installed', label: 'Installed' },
|
||||||
|
@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
|
|
||||||
import { PluginError, PluginType, unEscapeStringFromRegex } from '@grafana/data';
|
import { PluginError, PluginType, unEscapeStringFromRegex } from '@grafana/data';
|
||||||
|
|
||||||
|
import { filterByKeyword } from '../helpers';
|
||||||
import { RequestStatus, PluginCatalogStoreState } from '../types';
|
import { RequestStatus, PluginCatalogStoreState } from '../types';
|
||||||
|
|
||||||
import { pluginsAdapter } from './reducer';
|
import { pluginsAdapter } from './reducer';
|
||||||
@ -32,11 +33,14 @@ export type PluginFilters = {
|
|||||||
export const selectPlugins = (filters: PluginFilters) =>
|
export const selectPlugins = (filters: PluginFilters) =>
|
||||||
createSelector(selectAll, (plugins) => {
|
createSelector(selectAll, (plugins) => {
|
||||||
const keyword = filters.keyword ? unEscapeStringFromRegex(filters.keyword.toLowerCase()) : '';
|
const keyword = filters.keyword ? unEscapeStringFromRegex(filters.keyword.toLowerCase()) : '';
|
||||||
|
const filteredPluginIds = keyword !== '' ? filterByKeyword(plugins, keyword) : null;
|
||||||
|
|
||||||
return plugins.filter((plugin) => {
|
return plugins.filter((plugin) => {
|
||||||
const fieldsToSearchIn = [plugin.name, plugin.orgName].filter(Boolean).map((f) => f.toLowerCase());
|
if (keyword && filteredPluginIds == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (keyword && !fieldsToSearchIn.some((f) => f.includes(keyword))) {
|
if (keyword && !filteredPluginIds?.includes(plugin.id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ export interface CatalogPluginInfo {
|
|||||||
large: string;
|
large: string;
|
||||||
small: string;
|
small: string;
|
||||||
};
|
};
|
||||||
|
keywords: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RemotePlugin = {
|
export type RemotePlugin = {
|
||||||
@ -93,6 +94,7 @@ export type RemotePlugin = {
|
|||||||
featured: number;
|
featured: number;
|
||||||
id: number;
|
id: number;
|
||||||
internal: boolean;
|
internal: boolean;
|
||||||
|
keywords: string[];
|
||||||
json?: {
|
json?: {
|
||||||
dependencies: PluginDependencies;
|
dependencies: PluginDependencies;
|
||||||
iam?: IdentityAccessManagement;
|
iam?: IdentityAccessManagement;
|
||||||
@ -161,6 +163,7 @@ export type LocalPlugin = WithAccessControlMetadata & {
|
|||||||
small: string;
|
small: string;
|
||||||
large: string;
|
large: string;
|
||||||
};
|
};
|
||||||
|
keywords: string[];
|
||||||
build: Build;
|
build: Build;
|
||||||
screenshots?: Array<{
|
screenshots?: Array<{
|
||||||
path: string;
|
path: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user