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:
Yulia Shanyrova 2024-02-14 14:30:24 +01:00 committed by GitHub
parent cf65d91ee9
commit 9dcb7800de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 314 additions and 164 deletions

View File

@ -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{

View File

@ -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",

View File

@ -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 {

View File

@ -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))

View File

@ -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 {

View File

@ -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": "",

View File

@ -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,

View File

@ -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,

View File

@ -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',

View File

@ -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',

View File

@ -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',

View File

@ -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',

View File

@ -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',

View File

@ -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,

View File

@ -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]));
}

View File

@ -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();

View File

@ -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' },

View File

@ -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;
} }

View File

@ -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;