From 857649e30b9697ea5762dc4e02e6736627f0971e Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Mon, 23 Jan 2023 11:53:43 -0500 Subject: [PATCH 01/46] chore: move models/licensing into licensing service (#61878) --- pkg/api/http_server.go | 5 +++-- pkg/cmd/grafana-cli/runner/wireexts_oss.go | 2 +- pkg/plugins/licensing/licensing.go | 8 ++++---- pkg/server/wireexts_oss.go | 2 +- pkg/services/accesscontrol/acimpl/service_test.go | 4 ++-- .../ossaccesscontrol/permissions_services.go | 9 +++++---- .../accesscontrol/resourcepermissions/service.go | 6 +++--- pkg/services/featuremgmt/manager.go | 6 +++--- pkg/services/featuremgmt/service.go | 10 +++++----- pkg/services/featuremgmt/service_test.go | 7 ++++--- pkg/services/licensing/licensingtest/fake.go | 4 ++-- .../licensing.go => services/licensing/models.go} | 2 +- pkg/services/thumbs/service.go | 10 +++++----- 13 files changed, 39 insertions(+), 36 deletions(-) rename pkg/{models/licensing.go => services/licensing/models.go} (96%) diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 15a75846c23..7dd28a533e5 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -18,6 +18,7 @@ import ( "github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/folder" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/oauthtoken" "github.com/grafana/grafana/pkg/services/querylibrary" "github.com/grafana/grafana/pkg/services/searchV2" @@ -129,7 +130,7 @@ type HTTPServer struct { RemoteCacheService *remotecache.RemoteCache ProvisioningService provisioning.ProvisioningService Login login.Service - License models.Licensing + License licensing.Licensing AccessControl accesscontrol.AccessControl DataProxy *datasourceproxy.DataSourceProxyService PluginRequestValidator models.PluginRequestValidator @@ -220,7 +221,7 @@ type ServerOptions struct { } func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus, - renderService rendering.Service, licensing models.Licensing, hooksService *hooks.HooksService, + renderService rendering.Service, licensing licensing.Licensing, hooksService *hooks.HooksService, cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore, alertEngine *alerting.AlertEngine, pluginRequestValidator models.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver, pluginDashboardService plugindashboards.Service, pluginStore plugins.Store, pluginClient plugins.Client, diff --git a/pkg/cmd/grafana-cli/runner/wireexts_oss.go b/pkg/cmd/grafana-cli/runner/wireexts_oss.go index 930c1fcf31f..313cafad088 100644 --- a/pkg/cmd/grafana-cli/runner/wireexts_oss.go +++ b/pkg/cmd/grafana-cli/runner/wireexts_oss.go @@ -41,7 +41,7 @@ var wireExtsSet = wire.NewSet( wireSet, migrations.ProvideOSSMigrations, licensing.ProvideService, - wire.Bind(new(models.Licensing), new(*licensing.OSSLicensingService)), + wire.Bind(new(licensing.Licensing), new(*licensing.OSSLicensingService)), wire.Bind(new(registry.DatabaseMigrator), new(*migrations.OSSMigrations)), setting.ProvideProvider, wire.Bind(new(setting.Provider), new(*setting.OSSImpl)), diff --git a/pkg/plugins/licensing/licensing.go b/pkg/plugins/licensing/licensing.go index 2b17953ebf8..9a00e5828c2 100644 --- a/pkg/plugins/licensing/licensing.go +++ b/pkg/plugins/licensing/licensing.go @@ -3,16 +3,16 @@ package licensing import ( "fmt" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/setting" ) type Service struct { licensePath string - license models.Licensing + license licensing.Licensing } -func ProvideLicensing(cfg *setting.Cfg, l models.Licensing) *Service { +func ProvideLicensing(cfg *setting.Cfg, l licensing.Licensing) *Service { return &Service{ licensePath: cfg.EnterpriseLicensePath, license: l, @@ -21,7 +21,7 @@ func ProvideLicensing(cfg *setting.Cfg, l models.Licensing) *Service { func (l Service) Environment() []string { var env []string - if envProvider, ok := l.license.(models.LicenseEnvironment); ok { + if envProvider, ok := l.license.(licensing.LicenseEnvironment); ok { for k, v := range envProvider.Environment() { env = append(env, fmt.Sprintf("%s=%s", k, v)) } diff --git a/pkg/server/wireexts_oss.go b/pkg/server/wireexts_oss.go index b69c63daa7e..a8ea8aa44de 100644 --- a/pkg/server/wireexts_oss.go +++ b/pkg/server/wireexts_oss.go @@ -43,7 +43,7 @@ var wireExtsBasicSet = wire.NewSet( wire.Bind(new(auth.UserTokenService), new(*authimpl.UserAuthTokenService)), wire.Bind(new(auth.UserTokenBackgroundService), new(*authimpl.UserAuthTokenService)), licensing.ProvideService, - wire.Bind(new(models.Licensing), new(*licensing.OSSLicensingService)), + wire.Bind(new(licensing.Licensing), new(*licensing.OSSLicensingService)), setting.ProvideProvider, wire.Bind(new(setting.Provider), new(*setting.OSSImpl)), acimpl.ProvideService, diff --git a/pkg/services/accesscontrol/acimpl/service_test.go b/pkg/services/accesscontrol/acimpl/service_test.go index fe5e57f3473..06ab9b18d3d 100644 --- a/pkg/services/accesscontrol/acimpl/service_test.go +++ b/pkg/services/accesscontrol/acimpl/service_test.go @@ -11,13 +11,13 @@ import ( "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models/roletype" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/database" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" ) @@ -278,7 +278,7 @@ func TestService_DeclarePluginRoles(t *testing.T) { func TestService_RegisterFixedRoles(t *testing.T) { tests := []struct { name string - token models.Licensing + token licensing.Licensing registrations []accesscontrol.RoleRegistration wantErr bool }{ diff --git a/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go b/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go index bf3d8fe217c..a4ede7777ab 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts/retriever" "github.com/grafana/grafana/pkg/services/team" @@ -40,7 +41,7 @@ var ( func ProvideTeamPermissions( cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, - ac accesscontrol.AccessControl, license models.Licensing, service accesscontrol.Service, + ac accesscontrol.AccessControl, license licensing.Licensing, service accesscontrol.Service, teamService team.Service, userService user.Service, ) (*TeamPermissionsService, error) { options := resourcepermissions.Options{ @@ -114,7 +115,7 @@ var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.Act func ProvideDashboardPermissions( cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl, - license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service, + license licensing.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service, teamService team.Service, userService user.Service, ) (*DashboardPermissionsService, error) { getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*dashboards.Dashboard, error) { @@ -193,7 +194,7 @@ var FolderAdminActions = append(FolderEditActions, []string{dashboards.ActionFol func ProvideFolderPermissions( cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, accesscontrol accesscontrol.AccessControl, - license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service, + license licensing.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service, teamService team.Service, userService user.Service, ) (*FolderPermissionsService, error) { options := resourcepermissions.Options{ @@ -284,7 +285,7 @@ type ServiceAccountPermissionsService struct { func ProvideServiceAccountPermissions( cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl, - license models.Licensing, serviceAccountRetrieverService *retriever.Service, service accesscontrol.Service, + license licensing.Licensing, serviceAccountRetrieverService *retriever.Service, service accesscontrol.Service, teamService team.Service, userService user.Service, ) (*ServiceAccountPermissionsService, error) { options := resourcepermissions.Options{ diff --git a/pkg/services/accesscontrol/resourcepermissions/service.go b/pkg/services/accesscontrol/resourcepermissions/service.go index 8566492fd33..af56564f4a1 100644 --- a/pkg/services/accesscontrol/resourcepermissions/service.go +++ b/pkg/services/accesscontrol/resourcepermissions/service.go @@ -7,8 +7,8 @@ import ( "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/db" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/user" @@ -49,7 +49,7 @@ type Store interface { } func New( - options Options, cfg *setting.Cfg, router routing.RouteRegister, license models.Licensing, + options Options, cfg *setting.Cfg, router routing.RouteRegister, license licensing.Licensing, ac accesscontrol.AccessControl, service accesscontrol.Service, sqlStore db.DB, teamService team.Service, userService user.Service, ) (*Service, error) { @@ -104,7 +104,7 @@ type Service struct { service accesscontrol.Service store Store api *api - license models.Licensing + license licensing.Licensing options Options permissions []string diff --git a/pkg/services/featuremgmt/manager.go b/pkg/services/featuremgmt/manager.go index ad96139ff58..19a9f91c6e3 100644 --- a/pkg/services/featuremgmt/manager.go +++ b/pkg/services/featuremgmt/manager.go @@ -6,10 +6,10 @@ import ( "net/http" "reflect" - "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/licensing" ) var ( @@ -18,7 +18,7 @@ var ( type FeatureManager struct { isDevMod bool - licensing models.Licensing + licensing licensing.Licensing flags map[string]*FeatureFlag enabled map[string]bool // only the "on" values config string // path to config file diff --git a/pkg/services/featuremgmt/service.go b/pkg/services/featuremgmt/service.go index d3e5622f451..d963d9e2f28 100644 --- a/pkg/services/featuremgmt/service.go +++ b/pkg/services/featuremgmt/service.go @@ -5,12 +5,12 @@ import ( "os" "path/filepath" - "github.com/grafana/grafana/pkg/infra/log" - - "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/licensing" + "github.com/grafana/grafana/pkg/setting" ) var ( @@ -22,7 +22,7 @@ var ( }, []string{"name"}) ) -func ProvideManagerService(cfg *setting.Cfg, licensing models.Licensing) (*FeatureManager, error) { +func ProvideManagerService(cfg *setting.Cfg, licensing licensing.Licensing) (*FeatureManager, error) { mgmt := &FeatureManager{ isDevMod: setting.Env != setting.Prod, licensing: licensing, diff --git a/pkg/services/featuremgmt/service_test.go b/pkg/services/featuremgmt/service_test.go index 1085ea91f2c..660dbda8136 100644 --- a/pkg/services/featuremgmt/service_test.go +++ b/pkg/services/featuremgmt/service_test.go @@ -3,9 +3,10 @@ package featuremgmt import ( "testing" - "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/services/licensing" + "github.com/grafana/grafana/pkg/setting" ) func TestFeatureService(t *testing.T) { @@ -47,7 +48,7 @@ func TestFeatureService(t *testing.T) { } var ( - _ models.Licensing = (*stubLicenseServier)(nil) + _ licensing.Licensing = (*stubLicenseServier)(nil) ) type stubLicenseServier struct { diff --git a/pkg/services/licensing/licensingtest/fake.go b/pkg/services/licensing/licensingtest/fake.go index f5d8454ece1..7068217abea 100644 --- a/pkg/services/licensing/licensingtest/fake.go +++ b/pkg/services/licensing/licensingtest/fake.go @@ -3,10 +3,10 @@ package licensingtest import ( "github.com/stretchr/testify/mock" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/licensing" ) -var _ models.Licensing = new(FakeLicensing) +var _ licensing.Licensing = new(FakeLicensing) func NewFakeLicensing() *FakeLicensing { return &FakeLicensing{&mock.Mock{}} diff --git a/pkg/models/licensing.go b/pkg/services/licensing/models.go similarity index 96% rename from pkg/models/licensing.go rename to pkg/services/licensing/models.go index e49eb7a3288..4d2ad518f6b 100644 --- a/pkg/models/licensing.go +++ b/pkg/services/licensing/models.go @@ -1,4 +1,4 @@ -package models +package licensing type Licensing interface { // Expiry returns the unix epoch timestamp when the license expires, or 0 if no valid license is provided diff --git a/pkg/services/thumbs/service.go b/pkg/services/thumbs/service.go index a8ef7037cb6..da15dc8a1d2 100644 --- a/pkg/services/thumbs/service.go +++ b/pkg/services/thumbs/service.go @@ -10,9 +10,6 @@ import ( "github.com/segmentio/encoding/json" - "github.com/grafana/grafana/pkg/services/datasources/permissions" - "github.com/grafana/grafana/pkg/services/searchV2" - "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" @@ -20,10 +17,13 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/datasources/permissions" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/guardian" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/live" "github.com/grafana/grafana/pkg/services/rendering" + "github.com/grafana/grafana/pkg/services/searchV2" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/web" ) @@ -60,7 +60,7 @@ type thumbService struct { dashboardService dashboards.DashboardService dsUidsLookup getDatasourceUidsForDashboard dsPermissionsService permissions.DatasourcePermissionsService - licensing models.Licensing + licensing licensing.Licensing searchService searchV2.SearchService } @@ -78,7 +78,7 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, lockService *serverlock.ServerLockService, renderService rendering.Service, gl *live.GrafanaLive, store db.DB, authSetupService CrawlerAuthSetupService, dashboardService dashboards.DashboardService, dashboardThumbsService DashboardThumbService, searchService searchV2.SearchService, - dsPermissionsService permissions.DatasourcePermissionsService, licensing models.Licensing) Service { + dsPermissionsService permissions.DatasourcePermissionsService, licensing licensing.Licensing) Service { if !features.IsEnabled(featuremgmt.FlagDashboardPreviews) { return &dummyService{} } From 3eb065339f0fbb8d796f13f111408b8125a30b52 Mon Sep 17 00:00:00 2001 From: Joe Elliott Date: Mon, 23 Jan 2023 11:59:55 -0500 Subject: [PATCH 02/46] Tempo Datasource: Correct TraceQL docs link (#61931) * correct link Signed-off-by: Joe Elliott * lint Signed-off-by: Joe Elliott Signed-off-by: Joe Elliott --- public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx index 55f77ac41c0..8c3ae94adfe 100644 --- a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx @@ -25,11 +25,7 @@ export function QueryEditor(props: Props) { <> Build complex queries using TraceQL to select a list of traces.{' '} - + Documentation From 6b53f927b27a3f266d1d208980ad3fdcfcb557b1 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 23 Jan 2023 17:07:36 +0000 Subject: [PATCH 03/46] Navigation: truncate landing page descriptions to 3 lines (#61925) * truncate landing page descriptions to 3 lines * use correct css prop names --- .../app/core/components/AppChrome/NavLandingPage.tsx | 2 +- .../core/components/AppChrome/NavLandingPageCard.tsx | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/public/app/core/components/AppChrome/NavLandingPage.tsx b/public/app/core/components/AppChrome/NavLandingPage.tsx index 461e16cdd19..0143eebbaf0 100644 --- a/public/app/core/components/AppChrome/NavLandingPage.tsx +++ b/public/app/core/components/AppChrome/NavLandingPage.tsx @@ -51,7 +51,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ display: 'grid', gap: theme.spacing(3), gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', - gridAutoRows: '130px', + gridAutoRows: '138px', padding: theme.spacing(2, 0), }), }); diff --git a/public/app/core/components/AppChrome/NavLandingPageCard.tsx b/public/app/core/components/AppChrome/NavLandingPageCard.tsx index 8ed4a426729..23f582bdac3 100644 --- a/public/app/core/components/AppChrome/NavLandingPageCard.tsx +++ b/public/app/core/components/AppChrome/NavLandingPageCard.tsx @@ -15,7 +15,7 @@ export function NavLandingPageCard({ description, text, url }: Props) { return ( {text} - {description} + {description} ); } @@ -25,4 +25,12 @@ const getStyles = (theme: GrafanaTheme2) => ({ marginBottom: 0, gridTemplateRows: '1fr 0 2fr', }), + // Limit descriptions to 3 lines max before ellipsing + // Some plugin descriptions can be very long + description: css({ + WebkitLineClamp: 3, + WebkitBoxOrient: 'vertical', + display: '-webkit-box', + overflow: 'hidden', + }), }); From 7875fadd3121b77fb2d70919cff1c6e11a89cacd Mon Sep 17 00:00:00 2001 From: juanicabanas Date: Mon, 23 Jan 2023 14:50:19 -0300 Subject: [PATCH 04/46] GrafanaUI: Checkbox description fix (#61929) --- .../src/components/Forms/Checkbox.tsx | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.tsx b/packages/grafana-ui/src/components/Forms/Checkbox.tsx index 8cfc3c5312d..92186235865 100644 --- a/packages/grafana-ui/src/components/Forms/Checkbox.tsx +++ b/packages/grafana-ui/src/components/Forms/Checkbox.tsx @@ -30,19 +30,23 @@ export const Checkbox = React.forwardRef( return ( ); } @@ -55,6 +59,9 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { return { wrapper: css` + display: flex; + gap: ${theme.spacing(labelPadding)}; + align-items: baseline; position: relative; vertical-align: middle; font-size: 0; @@ -138,18 +145,17 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { css` position: relative; z-index: 2; - padding-left: ${theme.spacing(labelPadding)}; - white-space: nowrap; cursor: pointer; - position: relative; top: -3px; + max-width: fit-content; + line-height: ${theme.typography.bodySmall.lineHeight}; + margin-bottom: 0; ` ), description: cx( labelStyles.description, css` line-height: ${theme.typography.bodySmall.lineHeight}; - padding-left: ${theme.spacing(checkboxSize + labelPadding)}; margin-top: 0; /* The margin effectively comes from the top: -2px on the label above it */ ` ), From 48b620231e25887f264875c5b3520e9994fdf066 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Mon, 23 Jan 2023 13:03:44 -0500 Subject: [PATCH 05/46] Kindsys: Unique names for composable kind TS types (#61928) * Kindsys: Unique names for composable kind TS types * Update all TS imports --- pkg/plugins/codegen/jenny_plugintstypes.go | 6 +++--- public/app/features/scenes/builders/panelBuilders.ts | 2 +- public/app/plugins/panel/annolist/AnnoListPanel.test.tsx | 2 +- public/app/plugins/panel/annolist/AnnoListPanel.tsx | 2 +- public/app/plugins/panel/annolist/AnnotationListItem.tsx | 2 +- public/app/plugins/panel/annolist/module.tsx | 2 +- .../annolist/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/annolist/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/barchart/BarChartPanel.tsx | 2 +- public/app/plugins/panel/barchart/module.tsx | 2 +- .../barchart/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/barchart/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/barchart/suggestions.ts | 2 +- public/app/plugins/panel/barchart/utils.test.ts | 2 +- public/app/plugins/panel/barchart/utils.ts | 2 +- public/app/plugins/panel/bargauge/BarGaugeMigrations.ts | 2 +- public/app/plugins/panel/bargauge/BarGaugePanel.tsx | 2 +- public/app/plugins/panel/bargauge/module.tsx | 2 +- .../bargauge/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/bargauge/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/bargauge/suggestions.ts | 2 +- .../candlestick/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/canvas/{composable_panelcfg.cue => panelcfg.cue} | 0 public/app/plugins/panel/dashlist/DashList.tsx | 2 +- public/app/plugins/panel/dashlist/module.tsx | 2 +- .../dashlist/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/dashlist/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/gauge/GaugeMigrations.ts | 2 +- public/app/plugins/panel/gauge/GaugePanel.tsx | 2 +- public/app/plugins/panel/gauge/module.tsx | 2 +- .../panel/gauge/{composable_panelcfg.cue => panelcfg.cue} | 0 .../plugins/panel/gauge/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/gauge/suggestions.ts | 2 +- .../panel/heatmap/{composable_panelcfg.cue => panelcfg.cue} | 0 public/app/plugins/panel/histogram/Histogram.tsx | 2 +- public/app/plugins/panel/histogram/HistogramPanel.tsx | 2 +- public/app/plugins/panel/histogram/module.tsx | 2 +- .../histogram/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/histogram/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/news/NewsPanel.tsx | 2 +- public/app/plugins/panel/news/module.tsx | 2 +- .../panel/news/{composable_panelcfg.cue => panelcfg.cue} | 0 .../plugins/panel/news/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/piechart/PieChart.tsx | 2 +- public/app/plugins/panel/piechart/PieChartPanel.test.tsx | 2 +- public/app/plugins/panel/piechart/PieChartPanel.tsx | 2 +- public/app/plugins/panel/piechart/migrations.test.ts | 2 +- public/app/plugins/panel/piechart/migrations.ts | 2 +- public/app/plugins/panel/piechart/module.tsx | 2 +- .../piechart/{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/piechart/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/piechart/suggestions.ts | 2 +- public/app/plugins/panel/stat/StatMigrations.ts | 2 +- public/app/plugins/panel/stat/StatPanel.tsx | 2 +- public/app/plugins/panel/stat/module.tsx | 2 +- .../panel/stat/{composable_panelcfg.cue => panelcfg.cue} | 0 .../plugins/panel/stat/{models.gen.ts => panelcfg.gen.ts} | 0 public/app/plugins/panel/stat/suggestions.ts | 2 +- .../{composable_panelcfg.cue => panelcfg.cue} | 0 .../{composable_panelcfg.cue => panelcfg.cue} | 0 .../panel/table/{composable_panelcfg.cue => panelcfg.cue} | 0 public/app/plugins/panel/text/TextPanel.test.tsx | 2 +- public/app/plugins/panel/text/TextPanel.tsx | 2 +- public/app/plugins/panel/text/TextPanelEditor.tsx | 2 +- public/app/plugins/panel/text/module.tsx | 2 +- .../panel/text/{composable_panelcfg.cue => panelcfg.cue} | 0 .../plugins/panel/text/{models.gen.ts => panelcfg.gen.ts} | 0 .../plugins/panel/text/textPanelMigrationHandler.test.ts | 2 +- public/app/plugins/panel/text/textPanelMigrationHandler.ts | 2 +- .../timeseries/{composable_panelcfg.cue => panelcfg.cue} | 0 70 files changed, 45 insertions(+), 45 deletions(-) rename public/app/plugins/panel/annolist/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/annolist/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/barchart/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/barchart/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/bargauge/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/bargauge/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/candlestick/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/canvas/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/dashlist/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/dashlist/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/gauge/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/gauge/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/heatmap/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/histogram/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/histogram/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/news/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/news/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/piechart/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/piechart/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/stat/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/stat/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/state-timeline/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/status-history/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/table/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/text/{composable_panelcfg.cue => panelcfg.cue} (100%) rename public/app/plugins/panel/text/{models.gen.ts => panelcfg.gen.ts} (100%) rename public/app/plugins/panel/timeseries/{composable_panelcfg.cue => panelcfg.cue} (100%) diff --git a/pkg/plugins/codegen/jenny_plugintstypes.go b/pkg/plugins/codegen/jenny_plugintstypes.go index e76db2bea62..5f320d7229f 100644 --- a/pkg/plugins/codegen/jenny_plugintstypes.go +++ b/pkg/plugins/codegen/jenny_plugintstypes.go @@ -3,6 +3,7 @@ package codegen import ( "fmt" "path/filepath" + "strings" "github.com/grafana/codejen" tsast "github.com/grafana/cuetsy/ts/ast" @@ -40,11 +41,10 @@ func (j *ptsJenny) Generate(decl *pfs.PluginDecl) (*codejen.File, error) { } } - slotname := decl.SchemaInterface.Name() v := decl.Lineage.Latest().Version() tsf.Nodes = append(tsf.Nodes, tsast.Raw{ - Data: fmt.Sprintf("export const %sModelVersion = Object.freeze([%v, %v]);", slotname, v[0], v[1]), + Data: fmt.Sprintf("export const %sModelVersion = Object.freeze([%v, %v]);", decl.SchemaInterface.Name(), v[0], v[1]), }) jf, err := j.inner.Generate(decl) @@ -56,7 +56,7 @@ func (j *ptsJenny) Generate(decl *pfs.PluginDecl) (*codejen.File, error) { Data: string(jf.Data), }) - path := filepath.Join(j.root, decl.PluginPath, "models.gen.ts") + path := filepath.Join(j.root, decl.PluginPath, fmt.Sprintf("%s.gen.ts", strings.ToLower(decl.SchemaInterface.Name()))) data := []byte(tsf.String()) data = data[:len(data)-1] // remove the additional line break added by the inner jenny diff --git a/public/app/features/scenes/builders/panelBuilders.ts b/public/app/features/scenes/builders/panelBuilders.ts index d0c1247148d..cb61d937e3b 100644 --- a/public/app/features/scenes/builders/panelBuilders.ts +++ b/public/app/features/scenes/builders/panelBuilders.ts @@ -1,6 +1,6 @@ import { VizPanel, VizPanelState } from '@grafana/scenes'; import { GraphFieldConfig, TableFieldOptions } from '@grafana/schema'; -import { PanelOptions as BarGaugePanelOptions } from 'app/plugins/panel/bargauge/models.gen'; +import { PanelOptions as BarGaugePanelOptions } from 'app/plugins/panel/bargauge/panelcfg.gen'; import { PanelOptions as TablePanelOptions } from 'app/plugins/panel/table/models.gen'; import { TimeSeriesOptions } from 'app/plugins/panel/timeseries/types'; diff --git a/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx b/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx index c6689e688cb..e4ed97d90b6 100644 --- a/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx +++ b/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx @@ -10,7 +10,7 @@ import { backendSrv } from '../../../core/services/backend_srv'; import { setDashboardSrv } from '../../../features/dashboard/services/DashboardSrv'; import { AnnoListPanel, Props } from './AnnoListPanel'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; jest.mock('@grafana/runtime', () => ({ ...(jest.requireActual('@grafana/runtime') as unknown as object), diff --git a/public/app/plugins/panel/annolist/AnnoListPanel.tsx b/public/app/plugins/panel/annolist/AnnoListPanel.tsx index 8da05dae51e..66b6543a9d9 100644 --- a/public/app/plugins/panel/annolist/AnnoListPanel.tsx +++ b/public/app/plugins/panel/annolist/AnnoListPanel.tsx @@ -19,7 +19,7 @@ import appEvents from 'app/core/app_events'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { AnnotationListItem } from './AnnotationListItem'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; interface UserInfo { id?: number; diff --git a/public/app/plugins/panel/annolist/AnnotationListItem.tsx b/public/app/plugins/panel/annolist/AnnotationListItem.tsx index 1774e8d4644..0d50dbc0e6a 100644 --- a/public/app/plugins/panel/annolist/AnnotationListItem.tsx +++ b/public/app/plugins/panel/annolist/AnnotationListItem.tsx @@ -4,7 +4,7 @@ import React, { MouseEvent } from 'react'; import { AnnotationEvent, DateTimeInput, GrafanaTheme2, PanelProps } from '@grafana/data'; import { Card, TagList, Tooltip, RenderUserContentAsHTML, useStyles2 } from '@grafana/ui'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; interface Props extends Pick, 'options'> { annotation: AnnotationEvent; diff --git a/public/app/plugins/panel/annolist/module.tsx b/public/app/plugins/panel/annolist/module.tsx index bda4d62cbf3..0a796ccbbcb 100644 --- a/public/app/plugins/panel/annolist/module.tsx +++ b/public/app/plugins/panel/annolist/module.tsx @@ -4,7 +4,7 @@ import { PanelModel, PanelPlugin } from '@grafana/data'; import { TagsInput } from '@grafana/ui'; import { AnnoListPanel } from './AnnoListPanel'; -import { defaultPanelOptions, PanelOptions } from './models.gen'; +import { defaultPanelOptions, PanelOptions } from './panelcfg.gen'; export const plugin = new PanelPlugin(AnnoListPanel) .setPanelOptions((builder) => { diff --git a/public/app/plugins/panel/annolist/composable_panelcfg.cue b/public/app/plugins/panel/annolist/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/annolist/composable_panelcfg.cue rename to public/app/plugins/panel/annolist/panelcfg.cue diff --git a/public/app/plugins/panel/annolist/models.gen.ts b/public/app/plugins/panel/annolist/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/annolist/models.gen.ts rename to public/app/plugins/panel/annolist/panelcfg.gen.ts diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx index f94abde5c03..d1a680bae88 100644 --- a/public/app/plugins/panel/barchart/BarChartPanel.tsx +++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx @@ -37,7 +37,7 @@ import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; import { DataHoverView } from '../geomap/components/DataHoverView'; import { getFieldLegendItem } from '../state-timeline/utils'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; import { prepareBarChartDisplayValues, preparePlotConfigBuilder } from './utils'; const TOOLTIP_OFFSET = 10; diff --git a/public/app/plugins/panel/barchart/module.tsx b/public/app/plugins/panel/barchart/module.tsx index 7f862d1d691..3493b6cc001 100644 --- a/public/app/plugins/panel/barchart/module.tsx +++ b/public/app/plugins/panel/barchart/module.tsx @@ -16,7 +16,7 @@ import { ThresholdsStyleEditor } from '../timeseries/ThresholdsStyleEditor'; import { BarChartPanel } from './BarChartPanel'; import { TickSpacingEditor } from './TickSpacingEditor'; -import { PanelFieldConfig, PanelOptions, defaultPanelFieldConfig, defaultPanelOptions } from './models.gen'; +import { PanelFieldConfig, PanelOptions, defaultPanelFieldConfig, defaultPanelOptions } from './panelcfg.gen'; import { BarChartSuggestionsSupplier } from './suggestions'; import { prepareBarChartDisplayValues } from './utils'; diff --git a/public/app/plugins/panel/barchart/composable_panelcfg.cue b/public/app/plugins/panel/barchart/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/barchart/composable_panelcfg.cue rename to public/app/plugins/panel/barchart/panelcfg.cue diff --git a/public/app/plugins/panel/barchart/models.gen.ts b/public/app/plugins/panel/barchart/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/barchart/models.gen.ts rename to public/app/plugins/panel/barchart/panelcfg.gen.ts diff --git a/public/app/plugins/panel/barchart/suggestions.ts b/public/app/plugins/panel/barchart/suggestions.ts index 150696b60ad..c850ccf8152 100644 --- a/public/app/plugins/panel/barchart/suggestions.ts +++ b/public/app/plugins/panel/barchart/suggestions.ts @@ -2,7 +2,7 @@ import { VisualizationSuggestionsBuilder, VizOrientation } from '@grafana/data'; import { LegendDisplayMode, StackingMode, VisibilityMode } from '@grafana/schema'; import { SuggestionName } from 'app/types/suggestions'; -import { PanelFieldConfig, PanelOptions } from './models.gen'; +import { PanelFieldConfig, PanelOptions } from './panelcfg.gen'; export class BarChartSuggestionsSupplier { getListWithDefaults(builder: VisualizationSuggestionsBuilder) { diff --git a/public/app/plugins/panel/barchart/utils.test.ts b/public/app/plugins/panel/barchart/utils.test.ts index 670d49dd659..3483d122044 100644 --- a/public/app/plugins/panel/barchart/utils.test.ts +++ b/public/app/plugins/panel/barchart/utils.test.ts @@ -19,7 +19,7 @@ import { SortOrder, } from '@grafana/schema'; -import { PanelFieldConfig, PanelOptions } from './models.gen'; +import { PanelFieldConfig, PanelOptions } from './panelcfg.gen'; import { BarChartOptionsEX, prepareBarChartDisplayValues, preparePlotConfigBuilder } from './utils'; function mockDataFrame() { diff --git a/public/app/plugins/panel/barchart/utils.ts b/public/app/plugins/panel/barchart/utils.ts index 8c72fb9a19e..56e157b4c43 100644 --- a/public/app/plugins/panel/barchart/utils.ts +++ b/public/app/plugins/panel/barchart/utils.ts @@ -32,7 +32,7 @@ import { getStackingGroups } from '@grafana/ui/src/components/uPlot/utils'; import { findField } from 'app/features/dimensions'; import { BarsOptions, getConfig } from './bars'; -import { PanelFieldConfig, PanelOptions, defaultPanelFieldConfig } from './models.gen'; +import { PanelFieldConfig, PanelOptions, defaultPanelFieldConfig } from './panelcfg.gen'; import { BarChartDisplayValues, BarChartDisplayWarning } from './types'; function getBarCharScaleOrientation(orientation: VizOrientation) { diff --git a/public/app/plugins/panel/bargauge/BarGaugeMigrations.ts b/public/app/plugins/panel/bargauge/BarGaugeMigrations.ts index ca4ade8cf7d..ff07dc838b4 100644 --- a/public/app/plugins/panel/bargauge/BarGaugeMigrations.ts +++ b/public/app/plugins/panel/bargauge/BarGaugeMigrations.ts @@ -1,7 +1,7 @@ import { PanelModel } from '@grafana/data'; import { sharedSingleStatMigrationHandler } from '@grafana/ui'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export const barGaugePanelMigrationHandler = (panel: PanelModel): Partial => { return sharedSingleStatMigrationHandler(panel); diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 496d9c41786..0165cf3677f 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -16,7 +16,7 @@ import { BarGauge, DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProp import { DataLinksContextMenuApi } from '@grafana/ui/src/components/DataLinks/DataLinksContextMenu'; import { config } from 'app/core/config'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export class BarGaugePanel extends PureComponent { renderComponent = ( diff --git a/public/app/plugins/panel/bargauge/module.tsx b/public/app/plugins/panel/bargauge/module.tsx index 43295226fee..bfe41e90853 100644 --- a/public/app/plugins/panel/bargauge/module.tsx +++ b/public/app/plugins/panel/bargauge/module.tsx @@ -6,7 +6,7 @@ import { addOrientationOption, addStandardDataReduceOptions } from '../stat/comm import { barGaugePanelMigrationHandler } from './BarGaugeMigrations'; import { BarGaugePanel } from './BarGaugePanel'; -import { PanelOptions, defaultPanelOptions } from './models.gen'; +import { PanelOptions, defaultPanelOptions } from './panelcfg.gen'; import { BarGaugeSuggestionsSupplier } from './suggestions'; export const plugin = new PanelPlugin(BarGaugePanel) diff --git a/public/app/plugins/panel/bargauge/composable_panelcfg.cue b/public/app/plugins/panel/bargauge/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/bargauge/composable_panelcfg.cue rename to public/app/plugins/panel/bargauge/panelcfg.cue diff --git a/public/app/plugins/panel/bargauge/models.gen.ts b/public/app/plugins/panel/bargauge/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/bargauge/models.gen.ts rename to public/app/plugins/panel/bargauge/panelcfg.gen.ts diff --git a/public/app/plugins/panel/bargauge/suggestions.ts b/public/app/plugins/panel/bargauge/suggestions.ts index 31df170035e..2fc434f37df 100644 --- a/public/app/plugins/panel/bargauge/suggestions.ts +++ b/public/app/plugins/panel/bargauge/suggestions.ts @@ -2,7 +2,7 @@ import { VisualizationSuggestionsBuilder, VizOrientation } from '@grafana/data'; import { BarGaugeDisplayMode } from '@grafana/ui'; import { SuggestionName } from 'app/types/suggestions'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export class BarGaugeSuggestionsSupplier { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { diff --git a/public/app/plugins/panel/candlestick/composable_panelcfg.cue b/public/app/plugins/panel/candlestick/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/candlestick/composable_panelcfg.cue rename to public/app/plugins/panel/candlestick/panelcfg.cue diff --git a/public/app/plugins/panel/canvas/composable_panelcfg.cue b/public/app/plugins/panel/canvas/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/canvas/composable_panelcfg.cue rename to public/app/plugins/panel/canvas/panelcfg.cue diff --git a/public/app/plugins/panel/dashlist/DashList.tsx b/public/app/plugins/panel/dashlist/DashList.tsx index ca3e5824553..d03431ee639 100644 --- a/public/app/plugins/panel/dashlist/DashList.tsx +++ b/public/app/plugins/panel/dashlist/DashList.tsx @@ -14,7 +14,7 @@ import { SearchCard } from 'app/features/search/components/SearchCard'; import { DashboardSearchItem } from 'app/features/search/types'; import { useDispatch } from 'app/types'; -import { PanelLayout, PanelOptions } from './models.gen'; +import { PanelLayout, PanelOptions } from './panelcfg.gen'; import { getStyles } from './styles'; type Dashboard = DashboardSearchItem & { id?: number; isSearchResult?: boolean; isRecent?: boolean }; diff --git a/public/app/plugins/panel/dashlist/module.tsx b/public/app/plugins/panel/dashlist/module.tsx index d0fa3e8c036..fe6e735a284 100644 --- a/public/app/plugins/panel/dashlist/module.tsx +++ b/public/app/plugins/panel/dashlist/module.tsx @@ -11,7 +11,7 @@ import { } from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker'; import { DashList } from './DashList'; -import { defaultPanelOptions, PanelLayout, PanelOptions } from './models.gen'; +import { defaultPanelOptions, PanelLayout, PanelOptions } from './panelcfg.gen'; export const plugin = new PanelPlugin(DashList) .setPanelOptions((builder) => { diff --git a/public/app/plugins/panel/dashlist/composable_panelcfg.cue b/public/app/plugins/panel/dashlist/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/dashlist/composable_panelcfg.cue rename to public/app/plugins/panel/dashlist/panelcfg.cue diff --git a/public/app/plugins/panel/dashlist/models.gen.ts b/public/app/plugins/panel/dashlist/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/dashlist/models.gen.ts rename to public/app/plugins/panel/dashlist/panelcfg.gen.ts diff --git a/public/app/plugins/panel/gauge/GaugeMigrations.ts b/public/app/plugins/panel/gauge/GaugeMigrations.ts index 2ca30ab73a9..fbe6a33da67 100644 --- a/public/app/plugins/panel/gauge/GaugeMigrations.ts +++ b/public/app/plugins/panel/gauge/GaugeMigrations.ts @@ -1,7 +1,7 @@ import { PanelModel } from '@grafana/data'; import { sharedSingleStatPanelChangedHandler, sharedSingleStatMigrationHandler } from '@grafana/ui'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; // This is called when the panel first loads export const gaugePanelMigrationHandler = (panel: PanelModel): Partial => { diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index bbe3094e5cf..8254065cd0b 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -7,7 +7,7 @@ import { config } from 'app/core/config'; import { clearNameForSingleSeries } from '../bargauge/BarGaugePanel'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export class GaugePanel extends PureComponent> { renderComponent = ( diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 0910083e231..f101553107f 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -5,7 +5,7 @@ import { addOrientationOption, addStandardDataReduceOptions } from '../stat/comm import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations'; import { GaugePanel } from './GaugePanel'; -import { PanelOptions, defaultPanelOptions } from './models.gen'; +import { PanelOptions, defaultPanelOptions } from './panelcfg.gen'; import { GaugeSuggestionsSupplier } from './suggestions'; export const plugin = new PanelPlugin(GaugePanel) diff --git a/public/app/plugins/panel/gauge/composable_panelcfg.cue b/public/app/plugins/panel/gauge/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/gauge/composable_panelcfg.cue rename to public/app/plugins/panel/gauge/panelcfg.cue diff --git a/public/app/plugins/panel/gauge/models.gen.ts b/public/app/plugins/panel/gauge/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/gauge/models.gen.ts rename to public/app/plugins/panel/gauge/panelcfg.gen.ts diff --git a/public/app/plugins/panel/gauge/suggestions.ts b/public/app/plugins/panel/gauge/suggestions.ts index d0e075ffd91..6f9716c235f 100644 --- a/public/app/plugins/panel/gauge/suggestions.ts +++ b/public/app/plugins/panel/gauge/suggestions.ts @@ -1,7 +1,7 @@ import { ThresholdsMode, VisualizationSuggestionsBuilder } from '@grafana/data'; import { SuggestionName } from 'app/types/suggestions'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export class GaugeSuggestionsSupplier { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { diff --git a/public/app/plugins/panel/heatmap/composable_panelcfg.cue b/public/app/plugins/panel/heatmap/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/heatmap/composable_panelcfg.cue rename to public/app/plugins/panel/heatmap/panelcfg.cue diff --git a/public/app/plugins/panel/histogram/Histogram.tsx b/public/app/plugins/panel/histogram/Histogram.tsx index 0616e914829..8d4f6c43985 100644 --- a/public/app/plugins/panel/histogram/Histogram.tsx +++ b/public/app/plugins/panel/histogram/Histogram.tsx @@ -23,7 +23,7 @@ import { UPLOT_AXIS_FONT_SIZE, } from '@grafana/ui'; -import { defaultPanelFieldConfig, PanelFieldConfig, PanelOptions } from './models.gen'; +import { defaultPanelFieldConfig, PanelFieldConfig, PanelOptions } from './panelcfg.gen'; function incrRoundDn(num: number, incr: number) { return Math.floor(num / incr) * incr; diff --git a/public/app/plugins/panel/histogram/HistogramPanel.tsx b/public/app/plugins/panel/histogram/HistogramPanel.tsx index fba60c5c61d..84e6ff96eeb 100644 --- a/public/app/plugins/panel/histogram/HistogramPanel.tsx +++ b/public/app/plugins/panel/histogram/HistogramPanel.tsx @@ -5,7 +5,7 @@ import { histogramFieldsToFrame } from '@grafana/data/src/transformations/transf import { useTheme2 } from '@grafana/ui'; import { Histogram, getBucketSize } from './Histogram'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; type Props = PanelProps; diff --git a/public/app/plugins/panel/histogram/module.tsx b/public/app/plugins/panel/histogram/module.tsx index de820bb09ee..9b395ebeec4 100644 --- a/public/app/plugins/panel/histogram/module.tsx +++ b/public/app/plugins/panel/histogram/module.tsx @@ -3,7 +3,7 @@ import { histogramFieldInfo } from '@grafana/data/src/transformations/transforme import { commonOptionsBuilder, graphFieldOptions } from '@grafana/ui'; import { HistogramPanel } from './HistogramPanel'; -import { PanelFieldConfig, PanelOptions, defaultPanelFieldConfig, defaultPanelOptions } from './models.gen'; +import { PanelFieldConfig, PanelOptions, defaultPanelFieldConfig, defaultPanelOptions } from './panelcfg.gen'; import { originalDataHasHistogram } from './utils'; export const plugin = new PanelPlugin(HistogramPanel) diff --git a/public/app/plugins/panel/histogram/composable_panelcfg.cue b/public/app/plugins/panel/histogram/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/histogram/composable_panelcfg.cue rename to public/app/plugins/panel/histogram/panelcfg.cue diff --git a/public/app/plugins/panel/histogram/models.gen.ts b/public/app/plugins/panel/histogram/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/histogram/models.gen.ts rename to public/app/plugins/panel/histogram/panelcfg.gen.ts diff --git a/public/app/plugins/panel/news/NewsPanel.tsx b/public/app/plugins/panel/news/NewsPanel.tsx index 73ebd6adb22..a8a2a9453ab 100644 --- a/public/app/plugins/panel/news/NewsPanel.tsx +++ b/public/app/plugins/panel/news/NewsPanel.tsx @@ -6,7 +6,7 @@ import { CustomScrollbar } from '@grafana/ui'; import { News } from './component/News'; import { DEFAULT_FEED_URL } from './constants'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; import { useNewsFeed } from './useNewsFeed'; interface NewsPanelProps extends PanelProps {} diff --git a/public/app/plugins/panel/news/module.tsx b/public/app/plugins/panel/news/module.tsx index 13c2448b1ca..eead921f0a6 100644 --- a/public/app/plugins/panel/news/module.tsx +++ b/public/app/plugins/panel/news/module.tsx @@ -2,7 +2,7 @@ import { PanelPlugin } from '@grafana/data'; import { NewsPanel } from './NewsPanel'; import { DEFAULT_FEED_URL } from './constants'; -import { PanelOptions, defaultPanelOptions } from './models.gen'; +import { PanelOptions, defaultPanelOptions } from './panelcfg.gen'; export const plugin = new PanelPlugin(NewsPanel).setPanelOptions((builder) => { builder diff --git a/public/app/plugins/panel/news/composable_panelcfg.cue b/public/app/plugins/panel/news/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/news/composable_panelcfg.cue rename to public/app/plugins/panel/news/panelcfg.cue diff --git a/public/app/plugins/panel/news/models.gen.ts b/public/app/plugins/panel/news/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/news/models.gen.ts rename to public/app/plugins/panel/news/panelcfg.gen.ts diff --git a/public/app/plugins/panel/piechart/PieChart.tsx b/public/app/plugins/panel/piechart/PieChart.tsx index 80b35c811b8..13eb48e4ac4 100644 --- a/public/app/plugins/panel/piechart/PieChart.tsx +++ b/public/app/plugins/panel/piechart/PieChart.tsx @@ -29,7 +29,7 @@ import { import { getTooltipContainerStyles } from '@grafana/ui/src/themes/mixins'; import { useComponentInstanceId } from '@grafana/ui/src/utils/useComponetInstanceId'; -import { PieChartType, PieChartLabels } from './models.gen'; +import { PieChartType, PieChartLabels } from './panelcfg.gen'; import { filterDisplayItems, sumDisplayItemsReducer } from './utils'; /** diff --git a/public/app/plugins/panel/piechart/PieChartPanel.test.tsx b/public/app/plugins/panel/piechart/PieChartPanel.test.tsx index 787dde97f37..0b315565a5b 100644 --- a/public/app/plugins/panel/piechart/PieChartPanel.test.tsx +++ b/public/app/plugins/panel/piechart/PieChartPanel.test.tsx @@ -14,7 +14,7 @@ import { import { LegendDisplayMode, SortOrder, TooltipDisplayMode } from '@grafana/schema'; import { PieChartPanel } from './PieChartPanel'; -import { PanelOptions, PieChartType, PieChartLegendValues } from './models.gen'; +import { PanelOptions, PieChartType, PieChartLegendValues } from './panelcfg.gen'; type PieChartPanelProps = ComponentProps; diff --git a/public/app/plugins/panel/piechart/PieChartPanel.tsx b/public/app/plugins/panel/piechart/PieChartPanel.tsx index a4f92de212b..e4f9ff58eb3 100644 --- a/public/app/plugins/panel/piechart/PieChartPanel.tsx +++ b/public/app/plugins/panel/piechart/PieChartPanel.tsx @@ -22,7 +22,7 @@ import { } from '@grafana/ui'; import { PieChart } from './PieChart'; -import { PieChartLegendOptions, PieChartLegendValues, PanelOptions } from './models.gen'; +import { PieChartLegendOptions, PieChartLegendValues, PanelOptions } from './panelcfg.gen'; import { filterDisplayItems, sumDisplayItemsReducer } from './utils'; const defaultLegendOptions: PieChartLegendOptions = { diff --git a/public/app/plugins/panel/piechart/migrations.test.ts b/public/app/plugins/panel/piechart/migrations.test.ts index 82e1718e482..033b95167ac 100644 --- a/public/app/plugins/panel/piechart/migrations.test.ts +++ b/public/app/plugins/panel/piechart/migrations.test.ts @@ -1,7 +1,7 @@ import { FieldColorModeId, FieldConfigProperty, FieldMatcherID, PanelModel } from '@grafana/data'; import { PieChartPanelChangedHandler } from './migrations'; -import { PieChartLabels } from './models.gen'; +import { PieChartLabels } from './panelcfg.gen'; describe('PieChart -> PieChartV2 migrations', () => { it('only migrates old piechart', () => { diff --git a/public/app/plugins/panel/piechart/migrations.ts b/public/app/plugins/panel/piechart/migrations.ts index 86af4b938a4..04cbe4fae3d 100644 --- a/public/app/plugins/panel/piechart/migrations.ts +++ b/public/app/plugins/panel/piechart/migrations.ts @@ -1,7 +1,7 @@ import { FieldColorModeId, FieldConfigProperty, FieldMatcherID, PanelModel } from '@grafana/data'; import { LegendDisplayMode } from '@grafana/schema'; -import { PanelOptions, PieChartLabels, PieChartLegendValues, PieChartType } from './models.gen'; +import { PanelOptions, PieChartLabels, PieChartLegendValues, PieChartType } from './panelcfg.gen'; export const PieChartPanelChangedHandler = ( panel: PanelModel> | any, diff --git a/public/app/plugins/panel/piechart/module.tsx b/public/app/plugins/panel/piechart/module.tsx index dd60f40aeb2..b2d6a3634eb 100644 --- a/public/app/plugins/panel/piechart/module.tsx +++ b/public/app/plugins/panel/piechart/module.tsx @@ -5,7 +5,7 @@ import { addStandardDataReduceOptions } from '../stat/common'; import { PieChartPanel } from './PieChartPanel'; import { PieChartPanelChangedHandler } from './migrations'; -import { PanelOptions, PanelFieldConfig, PieChartType, PieChartLabels, PieChartLegendValues } from './models.gen'; +import { PanelOptions, PanelFieldConfig, PieChartType, PieChartLabels, PieChartLegendValues } from './panelcfg.gen'; import { PieChartSuggestionsSupplier } from './suggestions'; export const plugin = new PanelPlugin(PieChartPanel) diff --git a/public/app/plugins/panel/piechart/composable_panelcfg.cue b/public/app/plugins/panel/piechart/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/piechart/composable_panelcfg.cue rename to public/app/plugins/panel/piechart/panelcfg.cue diff --git a/public/app/plugins/panel/piechart/models.gen.ts b/public/app/plugins/panel/piechart/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/piechart/models.gen.ts rename to public/app/plugins/panel/piechart/panelcfg.gen.ts diff --git a/public/app/plugins/panel/piechart/suggestions.ts b/public/app/plugins/panel/piechart/suggestions.ts index 74c7a890a74..6bc1a86e095 100644 --- a/public/app/plugins/panel/piechart/suggestions.ts +++ b/public/app/plugins/panel/piechart/suggestions.ts @@ -1,7 +1,7 @@ import { VisualizationSuggestionsBuilder } from '@grafana/data'; import { SuggestionName } from 'app/types/suggestions'; -import { PieChartLabels, PanelOptions, PieChartType } from './models.gen'; +import { PieChartLabels, PanelOptions, PieChartType } from './panelcfg.gen'; export class PieChartSuggestionsSupplier { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { diff --git a/public/app/plugins/panel/stat/StatMigrations.ts b/public/app/plugins/panel/stat/StatMigrations.ts index d3be893181e..4348e3468df 100644 --- a/public/app/plugins/panel/stat/StatMigrations.ts +++ b/public/app/plugins/panel/stat/StatMigrations.ts @@ -2,7 +2,7 @@ import { FieldColorModeId, FieldConfigSource, PanelModel } from '@grafana/data'; import { BigValueTextMode, BigValueGraphMode, BigValueColorMode } from '@grafana/schema'; import { sharedSingleStatPanelChangedHandler } from '@grafana/ui'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; // This is called when the panel changes from another panel export const statPanelChangedHandler = ( diff --git a/public/app/plugins/panel/stat/StatPanel.tsx b/public/app/plugins/panel/stat/StatPanel.tsx index bb461112cfd..bbc29f28230 100644 --- a/public/app/plugins/panel/stat/StatPanel.tsx +++ b/public/app/plugins/panel/stat/StatPanel.tsx @@ -16,7 +16,7 @@ import { BigValue, DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProp import { DataLinksContextMenuApi } from '@grafana/ui/src/components/DataLinks/DataLinksContextMenu'; import { config } from 'app/core/config'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export class StatPanel extends PureComponent> { renderComponent = ( diff --git a/public/app/plugins/panel/stat/module.tsx b/public/app/plugins/panel/stat/module.tsx index d156f33540a..4f4884486b1 100644 --- a/public/app/plugins/panel/stat/module.tsx +++ b/public/app/plugins/panel/stat/module.tsx @@ -5,7 +5,7 @@ import { commonOptionsBuilder, sharedSingleStatMigrationHandler } from '@grafana import { statPanelChangedHandler } from './StatMigrations'; import { StatPanel } from './StatPanel'; import { addStandardDataReduceOptions, addOrientationOption } from './common'; -import { defaultPanelOptions, PanelOptions } from './models.gen'; +import { defaultPanelOptions, PanelOptions } from './panelcfg.gen'; import { StatSuggestionsSupplier } from './suggestions'; export const plugin = new PanelPlugin(StatPanel) diff --git a/public/app/plugins/panel/stat/composable_panelcfg.cue b/public/app/plugins/panel/stat/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/stat/composable_panelcfg.cue rename to public/app/plugins/panel/stat/panelcfg.cue diff --git a/public/app/plugins/panel/stat/models.gen.ts b/public/app/plugins/panel/stat/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/stat/models.gen.ts rename to public/app/plugins/panel/stat/panelcfg.gen.ts diff --git a/public/app/plugins/panel/stat/suggestions.ts b/public/app/plugins/panel/stat/suggestions.ts index 7b757bec003..7adc240be00 100644 --- a/public/app/plugins/panel/stat/suggestions.ts +++ b/public/app/plugins/panel/stat/suggestions.ts @@ -2,7 +2,7 @@ import { VisualizationSuggestionsBuilder } from '@grafana/data'; import { BigValueColorMode, BigValueGraphMode } from '@grafana/schema'; import { SuggestionName } from 'app/types/suggestions'; -import { PanelOptions } from './models.gen'; +import { PanelOptions } from './panelcfg.gen'; export class StatSuggestionsSupplier { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { diff --git a/public/app/plugins/panel/state-timeline/composable_panelcfg.cue b/public/app/plugins/panel/state-timeline/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/state-timeline/composable_panelcfg.cue rename to public/app/plugins/panel/state-timeline/panelcfg.cue diff --git a/public/app/plugins/panel/status-history/composable_panelcfg.cue b/public/app/plugins/panel/status-history/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/status-history/composable_panelcfg.cue rename to public/app/plugins/panel/status-history/panelcfg.cue diff --git a/public/app/plugins/panel/table/composable_panelcfg.cue b/public/app/plugins/panel/table/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/table/composable_panelcfg.cue rename to public/app/plugins/panel/table/panelcfg.cue diff --git a/public/app/plugins/panel/text/TextPanel.test.tsx b/public/app/plugins/panel/text/TextPanel.test.tsx index 99a245dff33..4e2967f7b9d 100644 --- a/public/app/plugins/panel/text/TextPanel.test.tsx +++ b/public/app/plugins/panel/text/TextPanel.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { dateTime, LoadingState, EventBusSrv } from '@grafana/data'; import { Props, TextPanel } from './TextPanel'; -import { TextMode } from './models.gen'; +import { TextMode } from './panelcfg.gen'; const replaceVariablesMock = jest.fn(); const defaultProps: Props = { diff --git a/public/app/plugins/panel/text/TextPanel.tsx b/public/app/plugins/panel/text/TextPanel.tsx index b3bca99353f..17afa29cf27 100644 --- a/public/app/plugins/panel/text/TextPanel.tsx +++ b/public/app/plugins/panel/text/TextPanel.tsx @@ -7,7 +7,7 @@ import { GrafanaTheme2, PanelProps, renderTextPanelMarkdown, textUtil, Interpola import { CustomScrollbar, CodeEditor, useStyles2 } from '@grafana/ui'; import config from 'app/core/config'; -import { defaultCodeOptions, PanelOptions, TextMode } from './models.gen'; +import { defaultCodeOptions, PanelOptions, TextMode } from './panelcfg.gen'; export interface Props extends PanelProps {} diff --git a/public/app/plugins/panel/text/TextPanelEditor.tsx b/public/app/plugins/panel/text/TextPanelEditor.tsx index 5a3d2417022..490251000ed 100644 --- a/public/app/plugins/panel/text/TextPanelEditor.tsx +++ b/public/app/plugins/panel/text/TextPanelEditor.tsx @@ -10,7 +10,7 @@ import { variableSuggestionToCodeEditorSuggestion, } from '@grafana/ui'; -import { PanelOptions, TextMode } from './models.gen'; +import { PanelOptions, TextMode } from './panelcfg.gen'; export const TextPanelEditor = ({ value, onChange, context }: StandardEditorProps) => { const language = useMemo(() => context.options?.mode ?? TextMode.Markdown, [context]); diff --git a/public/app/plugins/panel/text/module.tsx b/public/app/plugins/panel/text/module.tsx index 90535a49a3b..b4973f90a1b 100644 --- a/public/app/plugins/panel/text/module.tsx +++ b/public/app/plugins/panel/text/module.tsx @@ -2,7 +2,7 @@ import { PanelPlugin } from '@grafana/data'; import { TextPanel } from './TextPanel'; import { TextPanelEditor } from './TextPanelEditor'; -import { CodeLanguage, defaultCodeOptions, defaultPanelOptions, PanelOptions, TextMode } from './models.gen'; +import { CodeLanguage, defaultCodeOptions, defaultPanelOptions, PanelOptions, TextMode } from './panelcfg.gen'; import { textPanelMigrationHandler } from './textPanelMigrationHandler'; export const plugin = new PanelPlugin(TextPanel) diff --git a/public/app/plugins/panel/text/composable_panelcfg.cue b/public/app/plugins/panel/text/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/text/composable_panelcfg.cue rename to public/app/plugins/panel/text/panelcfg.cue diff --git a/public/app/plugins/panel/text/models.gen.ts b/public/app/plugins/panel/text/panelcfg.gen.ts similarity index 100% rename from public/app/plugins/panel/text/models.gen.ts rename to public/app/plugins/panel/text/panelcfg.gen.ts diff --git a/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts b/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts index 344d3a20a39..079e5cc8459 100644 --- a/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts +++ b/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts @@ -1,6 +1,6 @@ import { FieldConfigSource, PanelModel } from '@grafana/data'; -import { TextMode, PanelOptions } from './models.gen'; +import { TextMode, PanelOptions } from './panelcfg.gen'; import { textPanelMigrationHandler } from './textPanelMigrationHandler'; describe('textPanelMigrationHandler', () => { diff --git a/public/app/plugins/panel/text/textPanelMigrationHandler.ts b/public/app/plugins/panel/text/textPanelMigrationHandler.ts index bef50a9382d..77c52430620 100644 --- a/public/app/plugins/panel/text/textPanelMigrationHandler.ts +++ b/public/app/plugins/panel/text/textPanelMigrationHandler.ts @@ -1,6 +1,6 @@ import { PanelModel } from '@grafana/data'; -import { TextMode, PanelOptions } from './models.gen'; +import { TextMode, PanelOptions } from './panelcfg.gen'; export const textPanelMigrationHandler = (panel: PanelModel): Partial => { const previousVersion = parseFloat(panel.pluginVersion || '6.1'); diff --git a/public/app/plugins/panel/timeseries/composable_panelcfg.cue b/public/app/plugins/panel/timeseries/panelcfg.cue similarity index 100% rename from public/app/plugins/panel/timeseries/composable_panelcfg.cue rename to public/app/plugins/panel/timeseries/panelcfg.cue From 6e9eb0d9315ce4feee3ab5021a7de5e8aa4856e6 Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Mon, 23 Jan 2023 13:56:20 -0500 Subject: [PATCH 06/46] chore: move plugins models into pluginsettings svc (#61944) --- pkg/api/plugins.go | 5 +- pkg/models/plugin_settings.go | 70 ------------------- pkg/plugins/plugincontext/plugincontext.go | 5 +- .../service/dashboard_updater.go | 3 +- .../service/dashboard_updater_test.go | 18 ++--- pkg/services/pluginsettings/fake.go | 6 +- pkg/services/pluginsettings/models.go | 65 +++++++++++++++++ .../pluginsettings/service/service.go | 29 ++++---- .../pluginsettings/service/service_test.go | 11 ++- .../provisioning/plugins/config_reader.go | 3 +- .../provisioning/plugins/mocks/Store.go | 58 --------------- .../plugins/plugin_provisioner.go | 3 +- .../plugins/plugin_provisioner_test.go | 9 ++- 13 files changed, 108 insertions(+), 177 deletions(-) delete mode 100644 pkg/models/plugin_settings.go delete mode 100644 pkg/services/provisioning/plugins/mocks/Store.go diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index e66574085a9..757c39fd999 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -15,6 +15,7 @@ import ( "strings" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/models" @@ -202,7 +203,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon OrgID: c.OrgID, }) if err != nil { - if !errors.Is(err, models.ErrPluginSettingNotFound) { + if !errors.Is(err, pluginsettings.ErrPluginSettingNotFound) { return response.Error(http.StatusInternalServerError, "Failed to get plugin settings", nil) } } else { @@ -227,7 +228,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon } func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext) response.Response { - cmd := models.UpdatePluginSettingCmd{} + cmd := pluginsettings.UpdatePluginSettingCmd{} if err := web.Bind(c.Req, &cmd); err != nil { return response.Error(http.StatusBadRequest, "bad request data", err) } diff --git a/pkg/models/plugin_settings.go b/pkg/models/plugin_settings.go deleted file mode 100644 index e9e46191f03..00000000000 --- a/pkg/models/plugin_settings.go +++ /dev/null @@ -1,70 +0,0 @@ -package models - -import ( - "errors" - "time" -) - -var ( - ErrPluginSettingNotFound = errors.New("plugin setting not found") -) - -type PluginSetting struct { - Id int64 - PluginId string - OrgId int64 - Enabled bool - Pinned bool - JsonData map[string]interface{} - SecureJsonData map[string][]byte - PluginVersion string - - Created time.Time - Updated time.Time -} - -type PluginSettingInfo struct { - PluginID string `xorm:"plugin_id"` - OrgID int64 `xorm:"org_id"` - Enabled bool `xorm:"enabled"` - Pinned bool `xorm:"pinned"` - PluginVersion string `xorm:"plugin_id"` -} - -// ---------------------- -// COMMANDS - -// Also acts as api DTO -type UpdatePluginSettingCmd struct { - Enabled bool `json:"enabled"` - Pinned bool `json:"pinned"` - JsonData map[string]interface{} `json:"jsonData"` - SecureJsonData map[string]string `json:"secureJsonData"` - PluginVersion string `json:"version"` - - PluginId string `json:"-"` - OrgId int64 `json:"-"` - EncryptedSecureJsonData map[string][]byte `json:"-"` -} - -// specific command, will only update version -type UpdatePluginSettingVersionCmd struct { - PluginVersion string - PluginId string `json:"-"` - OrgId int64 `json:"-"` -} - -// --------------------- -// QUERIES - -type GetPluginSettingByIdQuery struct { - PluginId string - OrgId int64 - Result *PluginSetting -} - -type PluginStateChangedEvent struct { - PluginId string - OrgId int64 - Enabled bool -} diff --git a/pkg/plugins/plugincontext/plugincontext.go b/pkg/plugins/plugincontext/plugincontext.go index 97932755fb3..9abfd7ab7c6 100644 --- a/pkg/plugins/plugincontext/plugincontext.go +++ b/pkg/plugins/plugincontext/plugincontext.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/adapters" "github.com/grafana/grafana/pkg/services/datasources" @@ -80,9 +79,9 @@ func (p *Provider) pluginContext(ctx context.Context, pluginID string, user *use ps, err := p.getCachedPluginSettings(ctx, pluginID, user) if err != nil { - // models.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin). + // pluginsettings.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin). // If it's not this expected error something is wrong with cache or database and we return the error to the client. - if !errors.Is(err, models.ErrPluginSettingNotFound) { + if !errors.Is(err, pluginsettings.ErrPluginSettingNotFound) { return backend.PluginContext{}, false, fmt.Errorf("%v: %w", "Failed to get plugin settings", err) } } else { diff --git a/pkg/services/plugindashboards/service/dashboard_updater.go b/pkg/services/plugindashboards/service/dashboard_updater.go index 022e839879e..11a76ee07fb 100644 --- a/pkg/services/plugindashboards/service/dashboard_updater.go +++ b/pkg/services/plugindashboards/service/dashboard_updater.go @@ -6,7 +6,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/dashboardimport" @@ -132,7 +131,7 @@ func (du *DashboardUpdater) syncPluginDashboards(ctx context.Context, plugin plu } } -func (du *DashboardUpdater) handlePluginStateChanged(ctx context.Context, event *models.PluginStateChangedEvent) error { +func (du *DashboardUpdater) handlePluginStateChanged(ctx context.Context, event *pluginsettings.PluginStateChangedEvent) error { du.logger.Info("Plugin state changed", "pluginId", event.PluginId, "enabled", event.Enabled) if event.Enabled { diff --git a/pkg/services/plugindashboards/service/dashboard_updater_test.go b/pkg/services/plugindashboards/service/dashboard_updater_test.go index 80af3070b14..5b9e160e9f6 100644 --- a/pkg/services/plugindashboards/service/dashboard_updater_test.go +++ b/pkg/services/plugindashboards/service/dashboard_updater_test.go @@ -5,9 +5,10 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/tracing" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/dashboardimport" "github.com/grafana/grafana/pkg/services/dashboards" @@ -15,7 +16,6 @@ import ( "github.com/grafana/grafana/pkg/services/plugindashboards" "github.com/grafana/grafana/pkg/services/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsettings/service" - "github.com/stretchr/testify/require" ) func TestDashboardUpdater(t *testing.T) { @@ -202,7 +202,7 @@ func TestDashboardUpdater(t *testing.T) { t.Run("handlePluginStateChanged", func(t *testing.T) { scenario(t, "When app plugin is disabled that doesn't have any imported dashboards shouldn't delete any", scenarioInput{}, func(ctx *scenarioContext) { - err := ctx.bus.Publish(context.Background(), &models.PluginStateChangedEvent{ + err := ctx.bus.Publish(context.Background(), &pluginsettings.PluginStateChangedEvent{ PluginId: "test", OrgId: 2, Enabled: false, @@ -250,7 +250,7 @@ func TestDashboardUpdater(t *testing.T) { }, }, }, func(ctx *scenarioContext) { - err := ctx.bus.Publish(context.Background(), &models.PluginStateChangedEvent{ + err := ctx.bus.Publish(context.Background(), &pluginsettings.PluginStateChangedEvent{ PluginId: "test", OrgId: 2, Enabled: false, @@ -307,7 +307,7 @@ func TestDashboardUpdater(t *testing.T) { }, }, }, func(ctx *scenarioContext) { - err := ctx.bus.Publish(context.Background(), &models.PluginStateChangedEvent{ + err := ctx.bus.Publish(context.Background(), &pluginsettings.PluginStateChangedEvent{ PluginId: "test", OrgId: 2, Enabled: true, @@ -478,8 +478,8 @@ type scenarioContext struct { dashboardPluginService *dashboardPluginServiceMock dashboardService *dashboardServiceMock importDashboardArgs []*dashboardimport.ImportDashboardRequest - getPluginSettingsByIdArgs []*models.GetPluginSettingByIdQuery - updatePluginSettingVersionArgs []*models.UpdatePluginSettingVersionCmd + getPluginSettingsByIdArgs []*pluginsettings.GetPluginSettingByIdQuery + updatePluginSettingVersionArgs []*pluginsettings.UpdatePluginSettingVersionCmd dashboardUpdater *DashboardUpdater } @@ -492,8 +492,8 @@ func scenario(t *testing.T, desc string, input scenarioInput, f func(ctx *scenar t: t, bus: bus.ProvideBus(tracer), importDashboardArgs: []*dashboardimport.ImportDashboardRequest{}, - getPluginSettingsByIdArgs: []*models.GetPluginSettingByIdQuery{}, - updatePluginSettingVersionArgs: []*models.UpdatePluginSettingVersionCmd{}, + getPluginSettingsByIdArgs: []*pluginsettings.GetPluginSettingByIdQuery{}, + updatePluginSettingVersionArgs: []*pluginsettings.UpdatePluginSettingVersionCmd{}, } getPlugin := func(ctx context.Context, pluginID string) (plugins.PluginDTO, bool) { diff --git a/pkg/services/pluginsettings/fake.go b/pkg/services/pluginsettings/fake.go index 4a8f3cf4122..8504d69c275 100644 --- a/pkg/services/pluginsettings/fake.go +++ b/pkg/services/pluginsettings/fake.go @@ -3,8 +3,6 @@ package pluginsettings import ( "context" "time" - - "github.com/grafana/grafana/pkg/models" ) type FakePluginSettings struct { @@ -33,7 +31,7 @@ func (ps *FakePluginSettings) GetPluginSettingByPluginID(ctx context.Context, ar if res, ok := ps.Plugins[args.PluginID]; ok { return res, nil } - return nil, models.ErrPluginSettingNotFound + return nil, ErrPluginSettingNotFound } // UpdatePluginSetting updates a Plugin Setting @@ -66,7 +64,7 @@ func (ps *FakePluginSettings) UpdatePluginSettingPluginVersion(ctx context.Conte res.PluginVersion = args.PluginVersion return nil } - return models.ErrPluginSettingNotFound + return ErrPluginSettingNotFound } // DecryptedValues decrypts the encrypted secureJSONData of the provided plugin setting and diff --git a/pkg/services/pluginsettings/models.go b/pkg/services/pluginsettings/models.go index 52fec91129a..8b53a83e30d 100644 --- a/pkg/services/pluginsettings/models.go +++ b/pkg/services/pluginsettings/models.go @@ -1,9 +1,14 @@ package pluginsettings import ( + "errors" "time" ) +var ( + ErrPluginSettingNotFound = errors.New("plugin setting not found") +) + type DTO struct { ID int64 OrgID int64 @@ -49,3 +54,63 @@ type GetByPluginIDArgs struct { PluginID string OrgID int64 } + +type PluginSetting struct { + Id int64 + PluginId string + OrgId int64 + Enabled bool + Pinned bool + JsonData map[string]interface{} + SecureJsonData map[string][]byte + PluginVersion string + + Created time.Time + Updated time.Time +} + +type PluginSettingInfo struct { + PluginID string `xorm:"plugin_id"` + OrgID int64 `xorm:"org_id"` + Enabled bool `xorm:"enabled"` + Pinned bool `xorm:"pinned"` + PluginVersion string `xorm:"plugin_id"` +} + +// ---------------------- +// COMMANDS + +// Also acts as api DTO +type UpdatePluginSettingCmd struct { + Enabled bool `json:"enabled"` + Pinned bool `json:"pinned"` + JsonData map[string]interface{} `json:"jsonData"` + SecureJsonData map[string]string `json:"secureJsonData"` + PluginVersion string `json:"version"` + + PluginId string `json:"-"` + OrgId int64 `json:"-"` + EncryptedSecureJsonData map[string][]byte `json:"-"` +} + +// specific command, will only update version +type UpdatePluginSettingVersionCmd struct { + PluginVersion string + PluginId string `json:"-"` + OrgId int64 `json:"-"` +} + +// --------------------- +// QUERIES + +type GetPluginSettingByIdQuery struct { + PluginId string + OrgId int64 + Result *PluginSetting +} + +type PluginStateChangedEvent struct { + PluginId string + OrgId int64 + Enabled bool +} diff --git a/pkg/services/pluginsettings/service/service.go b/pkg/services/pluginsettings/service/service.go index 8ec61036d16..8c898822da8 100644 --- a/pkg/services/pluginsettings/service/service.go +++ b/pkg/services/pluginsettings/service/service.go @@ -7,7 +7,6 @@ import ( "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/pluginsettings" "github.com/grafana/grafana/pkg/services/secrets" ) @@ -64,7 +63,7 @@ func (s *Service) GetPluginSettings(ctx context.Context, args *pluginsettings.Ge } func (s *Service) GetPluginSettingByPluginID(ctx context.Context, args *pluginsettings.GetByPluginIDArgs) (*pluginsettings.DTO, error) { - query := &models.GetPluginSettingByIdQuery{ + query := &pluginsettings.GetPluginSettingByIdQuery{ OrgId: args.OrgID, PluginId: args.PluginID, } @@ -93,7 +92,7 @@ func (s *Service) UpdatePluginSetting(ctx context.Context, args *pluginsettings. return err } - return s.updatePluginSetting(ctx, &models.UpdatePluginSettingCmd{ + return s.updatePluginSetting(ctx, &pluginsettings.UpdatePluginSettingCmd{ Enabled: args.Enabled, Pinned: args.Pinned, JsonData: args.JSONData, @@ -106,7 +105,7 @@ func (s *Service) UpdatePluginSetting(ctx context.Context, args *pluginsettings. } func (s *Service) UpdatePluginSettingPluginVersion(ctx context.Context, args *pluginsettings.UpdatePluginVersionArgs) error { - return s.updatePluginSettingVersion(ctx, &models.UpdatePluginSettingVersionCmd{ + return s.updatePluginSettingVersion(ctx, &pluginsettings.UpdatePluginSettingVersionCmd{ PluginVersion: args.PluginVersion, PluginId: args.PluginID, OrgId: args.OrgID, @@ -135,7 +134,7 @@ func (s *Service) DecryptedValues(ps *pluginsettings.DTO) map[string]string { return json } -func (s *Service) getPluginSettingsInfo(ctx context.Context, orgID int64) ([]*models.PluginSettingInfo, error) { +func (s *Service) getPluginSettingsInfo(ctx context.Context, orgID int64) ([]*pluginsettings.PluginSettingInfo, error) { sql := `SELECT org_id, plugin_id, enabled, pinned, plugin_version FROM plugin_setting ` params := make([]interface{}, 0) @@ -144,7 +143,7 @@ func (s *Service) getPluginSettingsInfo(ctx context.Context, orgID int64) ([]*mo params = append(params, orgID) } - var rslt []*models.PluginSettingInfo + var rslt []*pluginsettings.PluginSettingInfo err := s.db.WithDbSession(ctx, func(sess *db.Session) error { return sess.SQL(sql, params...).Find(&rslt) }) @@ -155,23 +154,23 @@ func (s *Service) getPluginSettingsInfo(ctx context.Context, orgID int64) ([]*mo return rslt, nil } -func (s *Service) getPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error { +func (s *Service) getPluginSettingById(ctx context.Context, query *pluginsettings.GetPluginSettingByIdQuery) error { return s.db.WithDbSession(ctx, func(sess *db.Session) error { - pluginSetting := models.PluginSetting{OrgId: query.OrgId, PluginId: query.PluginId} + pluginSetting := pluginsettings.PluginSetting{OrgId: query.OrgId, PluginId: query.PluginId} has, err := sess.Get(&pluginSetting) if err != nil { return err } else if !has { - return models.ErrPluginSettingNotFound + return pluginsettings.ErrPluginSettingNotFound } query.Result = &pluginSetting return nil }) } -func (s *Service) updatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error { +func (s *Service) updatePluginSetting(ctx context.Context, cmd *pluginsettings.UpdatePluginSettingCmd) error { return s.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error { - var pluginSetting models.PluginSetting + var pluginSetting pluginsettings.PluginSetting exists, err := sess.Where("org_id=? and plugin_id=?", cmd.OrgId, cmd.PluginId).Get(&pluginSetting) if err != nil { @@ -180,7 +179,7 @@ func (s *Service) updatePluginSetting(ctx context.Context, cmd *models.UpdatePlu sess.UseBool("enabled") sess.UseBool("pinned") if !exists { - pluginSetting = models.PluginSetting{ + pluginSetting = pluginsettings.PluginSetting{ PluginId: cmd.PluginId, OrgId: cmd.OrgId, Enabled: cmd.Enabled, @@ -193,7 +192,7 @@ func (s *Service) updatePluginSetting(ctx context.Context, cmd *models.UpdatePlu } // add state change event on commit success - sess.PublishAfterCommit(&models.PluginStateChangedEvent{ + sess.PublishAfterCommit(&pluginsettings.PluginStateChangedEvent{ PluginId: cmd.PluginId, OrgId: cmd.OrgId, Enabled: cmd.Enabled, @@ -209,7 +208,7 @@ func (s *Service) updatePluginSetting(ctx context.Context, cmd *models.UpdatePlu // add state change event on commit success if pluginSetting.Enabled != cmd.Enabled { - sess.PublishAfterCommit(&models.PluginStateChangedEvent{ + sess.PublishAfterCommit(&pluginsettings.PluginStateChangedEvent{ PluginId: cmd.PluginId, OrgId: cmd.OrgId, Enabled: cmd.Enabled, @@ -227,7 +226,7 @@ func (s *Service) updatePluginSetting(ctx context.Context, cmd *models.UpdatePlu }) } -func (s *Service) updatePluginSettingVersion(ctx context.Context, cmd *models.UpdatePluginSettingVersionCmd) error { +func (s *Service) updatePluginSettingVersion(ctx context.Context, cmd *pluginsettings.UpdatePluginSettingVersionCmd) error { return s.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error { _, err := sess.Exec("UPDATE plugin_setting SET plugin_version=? WHERE org_id=? AND plugin_id=?", cmd.PluginVersion, cmd.OrgId, cmd.PluginId) return err diff --git a/pkg/services/pluginsettings/service/service_test.go b/pkg/services/pluginsettings/service/service_test.go index 99fb21f5266..35b6cd1fe75 100644 --- a/pkg/services/pluginsettings/service/service_test.go +++ b/pkg/services/pluginsettings/service/service_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/db" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/pluginsettings" "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets/fakes" @@ -106,7 +105,7 @@ func TestIntegrationPluginSettings(t *testing.T) { secureJsonData, err := secretsService.EncryptJsonData(context.Background(), map[string]string{"secureKey": "secureValue"}, secrets.WithoutScope()) require.NoError(t, err) - existing := models.PluginSetting{ + existing := pluginsettings.PluginSetting{ OrgId: 1, PluginId: "existing", Enabled: false, @@ -167,8 +166,8 @@ func TestIntegrationPluginSettings(t *testing.T) { }) t.Run("UpdatePluginSetting should update existing plugin settings and publish PluginStateChangedEvent", func(t *testing.T) { - var pluginStateChangedEvent *models.PluginStateChangedEvent - store.Bus().AddEventListener(func(_ context.Context, evt *models.PluginStateChangedEvent) error { + var pluginStateChangedEvent *pluginsettings.PluginStateChangedEvent + store.Bus().AddEventListener(func(_ context.Context, evt *pluginsettings.PluginStateChangedEvent) error { pluginStateChangedEvent = evt return nil }) @@ -225,8 +224,8 @@ func TestIntegrationPluginSettings(t *testing.T) { t.Run("Non-existing plugin settings", func(t *testing.T) { t.Run("UpdatePluginSetting should insert plugin settings and publish PluginStateChangedEvent", func(t *testing.T) { - var pluginStateChangedEvent *models.PluginStateChangedEvent - store.Bus().AddEventListener(func(_ context.Context, evt *models.PluginStateChangedEvent) error { + var pluginStateChangedEvent *pluginsettings.PluginStateChangedEvent + store.Bus().AddEventListener(func(_ context.Context, evt *pluginsettings.PluginStateChangedEvent) error { pluginStateChangedEvent = evt return nil }) diff --git a/pkg/services/provisioning/plugins/config_reader.go b/pkg/services/provisioning/plugins/config_reader.go index 13ea247da78..bb066cb2e0c 100644 --- a/pkg/services/provisioning/plugins/config_reader.go +++ b/pkg/services/provisioning/plugins/config_reader.go @@ -8,9 +8,10 @@ import ( "path/filepath" "strings" + "gopkg.in/yaml.v3" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" - "gopkg.in/yaml.v3" ) type configReader interface { diff --git a/pkg/services/provisioning/plugins/mocks/Store.go b/pkg/services/provisioning/plugins/mocks/Store.go deleted file mode 100644 index 698da31a9b3..00000000000 --- a/pkg/services/provisioning/plugins/mocks/Store.go +++ /dev/null @@ -1,58 +0,0 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - models "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/org" - mock "github.com/stretchr/testify/mock" -) - -// Store is an autogenerated mock type for the Store type -type Store struct { - mock.Mock -} - -// GetOrgByNameHandler provides a mock function with given fields: ctx, query -func (_m *Store) GetOrgByNameHandler(ctx context.Context, query *org.GetOrgByNameQuery) error { - ret := _m.Called(ctx, query) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *org.GetOrgByNameQuery) error); ok { - r0 = rf(ctx, query) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetPluginSettingById provides a mock function with given fields: ctx, query -func (_m *Store) GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error { - ret := _m.Called(ctx, query) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *models.GetPluginSettingByIdQuery) error); ok { - r0 = rf(ctx, query) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdatePluginSetting provides a mock function with given fields: ctx, cmd -func (_m *Store) UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error { - ret := _m.Called(ctx, cmd) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *models.UpdatePluginSettingCmd) error); ok { - r0 = rf(ctx, cmd) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/pkg/services/provisioning/plugins/plugin_provisioner.go b/pkg/services/provisioning/plugins/plugin_provisioner.go index 26d81c2f745..c154603eb0c 100644 --- a/pkg/services/provisioning/plugins/plugin_provisioner.go +++ b/pkg/services/provisioning/plugins/plugin_provisioner.go @@ -5,7 +5,6 @@ import ( "errors" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/pluginsettings" @@ -51,7 +50,7 @@ func (ap *PluginProvisioner) apply(ctx context.Context, cfg *pluginsAsConfig) er PluginID: app.PluginID, }) if err != nil { - if !errors.Is(err, models.ErrPluginSettingNotFound) { + if !errors.Is(err, pluginsettings.ErrPluginSettingNotFound) { return err } } else { diff --git a/pkg/services/provisioning/plugins/plugin_provisioner_test.go b/pkg/services/provisioning/plugins/plugin_provisioner_test.go index 5d4f8be3745..b805756a048 100644 --- a/pkg/services/provisioning/plugins/plugin_provisioner_test.go +++ b/pkg/services/provisioning/plugins/plugin_provisioner_test.go @@ -5,13 +5,12 @@ import ( "errors" "testing" + "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/pluginsettings" - - "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" - "github.com/stretchr/testify/require" ) func TestPluginProvisioner(t *testing.T) { @@ -91,7 +90,7 @@ func (m *mockStore) GetPluginSettingByPluginID(_ context.Context, args *pluginse }, nil } - return nil, models.ErrPluginSettingNotFound + return nil, pluginsettings.ErrPluginSettingNotFound } func (m *mockStore) UpdatePluginSetting(_ context.Context, args *pluginsettings.UpdateArgs) error { From 856abe12818af590620bc967e279a8c7feece794 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Mon, 23 Jan 2023 14:27:33 -0500 Subject: [PATCH 07/46] Kindsys: Add Ptr func (#61948) --- pkg/kindsys/util.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pkg/kindsys/util.go diff --git a/pkg/kindsys/util.go b/pkg/kindsys/util.go new file mode 100644 index 00000000000..b7d03085e79 --- /dev/null +++ b/pkg/kindsys/util.go @@ -0,0 +1,24 @@ +package kindsys + +// Ptr returns a pointer to a value of an arbitrary type. +// +// This function is provided to compensate for Grafana's Go code generation that +// represents optional fields using pointers. +// +// Pointers are the only technically [correct, non-ambiguous] way of +// representing an optional field in Go's type system. However, Go does not +// allow taking the address of certain primitive types inline. That is, +// this is invalid Go code: +// +// var str *string +// str = &"colorless green ideas sleep furiously" +// +// This func allows making such declarations in a single line: +// +// var str *string +// str = kindsys.Ptr("colorless green ideas sleep furiously") +// +// [correct, non-ambiguous]: https://github.com/grafana/grok/issues/1 +func Ptr[T any](v T) *T { + return &v +} From 17aaf9f0d37078fc4ee2982ddb5614f10db7acec Mon Sep 17 00:00:00 2001 From: sam boyer Date: Mon, 23 Jan 2023 14:28:44 -0500 Subject: [PATCH 08/46] panels: Remove redundant import package from cue defs (#61949) --- .../app/plugins/panel/barchart/panelcfg.cue | 22 ++++++++--------- .../plugins/panel/barchart/panelcfg.gen.ts | 24 +++++++++---------- .../app/plugins/panel/bargauge/panelcfg.cue | 6 ++--- .../plugins/panel/bargauge/panelcfg.gen.ts | 8 +++---- public/app/plugins/panel/gauge/panelcfg.cue | 4 ++-- .../app/plugins/panel/gauge/panelcfg.gen.ts | 4 ++-- .../app/plugins/panel/histogram/panelcfg.cue | 12 +++++----- .../plugins/panel/histogram/panelcfg.gen.ts | 10 ++++---- .../app/plugins/panel/piechart/panelcfg.cue | 10 ++++---- .../plugins/panel/piechart/panelcfg.gen.ts | 8 +++---- public/app/plugins/panel/stat/panelcfg.cue | 12 +++++----- public/app/plugins/panel/stat/panelcfg.gen.ts | 20 ++++++++-------- .../plugins/panel/state-timeline/panelcfg.cue | 12 +++++----- .../plugins/panel/status-history/panelcfg.cue | 12 +++++----- public/app/plugins/panel/table/panelcfg.cue | 6 ++--- .../app/plugins/panel/timeseries/panelcfg.cue | 8 +++---- 16 files changed, 89 insertions(+), 89 deletions(-) diff --git a/public/app/plugins/panel/barchart/panelcfg.cue b/public/app/plugins/panel/barchart/panelcfg.cue index 4dd9472da9d..e02aa406753 100644 --- a/public/app/plugins/panel/barchart/panelcfg.cue +++ b/public/app/plugins/panel/barchart/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -28,16 +28,16 @@ composableKinds: PanelCfg: { // v0.0 { PanelOptions: { - ui.OptionsWithLegend - ui.OptionsWithTooltip - ui.OptionsWithTextFormatting + common.OptionsWithLegend + common.OptionsWithTooltip + common.OptionsWithTextFormatting // Manually select which field from the dataset to represent the x field. xField?: string // Use the color value for a sibling field to color each bar value. colorByField?: string // Controls the orientation of the bar chart, either vertical or horizontal. - orientation: ui.VizOrientation | *"auto" + orientation: common.VizOrientation | *"auto" // Controls the radius of each bar. barRadius?: float64 & >=0 & <=0.5 | *0 // Controls the rotation of the x axis labels. @@ -48,9 +48,9 @@ composableKinds: PanelCfg: { // negative values indicate backwards skipping behavior xTickLabelSpacing?: int32 | *0 // Controls whether bars are stacked or not, either normally or in percent mode. - stacking: ui.StackingMode | *"none" + stacking: common.StackingMode | *"none" // This controls whether values are shown on top or to the left of bars. - showValue: ui.VisibilityMode | *"auto" + showValue: common.VisibilityMode | *"auto" // Controls the width of bars. 1 = Max width, 0 = Min width. barWidth: float64 & >=0 & <=1 | *0.97 // Controls the width of groups. 1 = max with, 0 = min width. @@ -60,8 +60,8 @@ composableKinds: PanelCfg: { fullHighlight: bool | *false } @cuetsy(kind="interface") PanelFieldConfig: { - ui.AxisConfig - ui.HideableFieldConfig + common.AxisConfig + common.HideableFieldConfig // Controls line width of the bars. lineWidth?: int32 & >=0 & <=10 | *1 @@ -69,9 +69,9 @@ composableKinds: PanelCfg: { fillOpacity?: int32 & >=0 & <=100 | *80 // Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. // Gradient appearance is influenced by the Fill opacity setting. - gradientMode?: ui.GraphGradientMode | *"none" + gradientMode?: common.GraphGradientMode | *"none" // Threshold rendering - thresholdsStyle?: ui.GraphThresholdsStyleConfig + thresholdsStyle?: common.GraphThresholdsStyleConfig } @cuetsy(kind="interface") }, ] diff --git a/public/app/plugins/panel/barchart/panelcfg.gen.ts b/public/app/plugins/panel/barchart/panelcfg.gen.ts index c38ba1c0800..e6a2c9e317f 100644 --- a/public/app/plugins/panel/barchart/panelcfg.gen.ts +++ b/public/app/plugins/panel/barchart/panelcfg.gen.ts @@ -8,11 +8,11 @@ // // Run 'make gen-cue' from repository root to regenerate. -import * as ui from '@grafana/schema'; +import * as common from '@grafana/schema'; export const PanelCfgModelVersion = Object.freeze([0, 0]); -export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTextFormatting { +export interface PanelOptions extends common.OptionsWithLegend, common.OptionsWithTooltip, common.OptionsWithTextFormatting { /** * Controls the radius of each bar. */ @@ -37,15 +37,15 @@ export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithToolti /** * Controls the orientation of the bar chart, either vertical or horizontal. */ - orientation: ui.VizOrientation; + orientation: common.VizOrientation; /** * This controls whether values are shown on top or to the left of bars. */ - showValue: ui.VisibilityMode; + showValue: common.VisibilityMode; /** * Controls whether bars are stacked or not, either normally or in percent mode. */ - stacking: ui.StackingMode; + stacking: common.StackingMode; /** * Manually select which field from the dataset to represent the x field. */ @@ -70,14 +70,14 @@ export const defaultPanelOptions: Partial = { barWidth: 0.97, fullHighlight: false, groupWidth: 0.7, - orientation: ui.VizOrientation.Auto, - showValue: ui.VisibilityMode.Auto, - stacking: ui.StackingMode.None, + orientation: common.VizOrientation.Auto, + showValue: common.VisibilityMode.Auto, + stacking: common.StackingMode.None, xTickLabelRotation: 0, xTickLabelSpacing: 0, }; -export interface PanelFieldConfig extends ui.AxisConfig, ui.HideableFieldConfig { +export interface PanelFieldConfig extends common.AxisConfig, common.HideableFieldConfig { /** * Controls the fill opacity of the bars. */ @@ -86,7 +86,7 @@ export interface PanelFieldConfig extends ui.AxisConfig, ui.HideableFieldConfig * Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. * Gradient appearance is influenced by the Fill opacity setting. */ - gradientMode?: ui.GraphGradientMode; + gradientMode?: common.GraphGradientMode; /** * Controls line width of the bars. */ @@ -94,11 +94,11 @@ export interface PanelFieldConfig extends ui.AxisConfig, ui.HideableFieldConfig /** * Threshold rendering */ - thresholdsStyle?: ui.GraphThresholdsStyleConfig; + thresholdsStyle?: common.GraphThresholdsStyleConfig; } export const defaultPanelFieldConfig: Partial = { fillOpacity: 80, - gradientMode: ui.GraphGradientMode.None, + gradientMode: common.GraphGradientMode.None, lineWidth: 1, }; diff --git a/public/app/plugins/panel/bargauge/panelcfg.cue b/public/app/plugins/panel/bargauge/panelcfg.cue index a2e70451ce0..73f133fb39c 100644 --- a/public/app/plugins/panel/bargauge/panelcfg.cue +++ b/public/app/plugins/panel/bargauge/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -27,8 +27,8 @@ composableKinds: PanelCfg: { schemas: [ { PanelOptions: { - ui.SingleStatBaseOptions - displayMode: ui.BarGaugeDisplayMode | *"gradient" + common.SingleStatBaseOptions + displayMode: common.BarGaugeDisplayMode | *"gradient" showUnfilled: bool | *true minVizWidth: uint32 | *0 minVizHeight: uint32 | *10 diff --git a/public/app/plugins/panel/bargauge/panelcfg.gen.ts b/public/app/plugins/panel/bargauge/panelcfg.gen.ts index 60b8b623e9b..f42c6814cfc 100644 --- a/public/app/plugins/panel/bargauge/panelcfg.gen.ts +++ b/public/app/plugins/panel/bargauge/panelcfg.gen.ts @@ -8,19 +8,19 @@ // // Run 'make gen-cue' from repository root to regenerate. -import * as ui from '@grafana/schema'; +import * as common from '@grafana/schema'; export const PanelCfgModelVersion = Object.freeze([0, 0]); -export interface PanelOptions extends ui.SingleStatBaseOptions { - displayMode: ui.BarGaugeDisplayMode; +export interface PanelOptions extends common.SingleStatBaseOptions { + displayMode: common.BarGaugeDisplayMode; minVizHeight: number; minVizWidth: number; showUnfilled: boolean; } export const defaultPanelOptions: Partial = { - displayMode: ui.BarGaugeDisplayMode.Gradient, + displayMode: common.BarGaugeDisplayMode.Gradient, minVizHeight: 10, minVizWidth: 0, showUnfilled: true, diff --git a/public/app/plugins/panel/gauge/panelcfg.cue b/public/app/plugins/panel/gauge/panelcfg.cue index 9468211647c..0c14160c8e3 100644 --- a/public/app/plugins/panel/gauge/panelcfg.cue +++ b/public/app/plugins/panel/gauge/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -27,7 +27,7 @@ composableKinds: PanelCfg: { schemas: [ { PanelOptions: { - ui.SingleStatBaseOptions + common.SingleStatBaseOptions showThresholdLabels: bool | *false showThresholdMarkers: bool | *true } @cuetsy(kind="interface") diff --git a/public/app/plugins/panel/gauge/panelcfg.gen.ts b/public/app/plugins/panel/gauge/panelcfg.gen.ts index 1da208ed498..9541d694bc5 100644 --- a/public/app/plugins/panel/gauge/panelcfg.gen.ts +++ b/public/app/plugins/panel/gauge/panelcfg.gen.ts @@ -8,11 +8,11 @@ // // Run 'make gen-cue' from repository root to regenerate. -import * as ui from '@grafana/schema'; +import * as common from '@grafana/schema'; export const PanelCfgModelVersion = Object.freeze([0, 0]); -export interface PanelOptions extends ui.SingleStatBaseOptions { +export interface PanelOptions extends common.SingleStatBaseOptions { showThresholdLabels: boolean; showThresholdMarkers: boolean; } diff --git a/public/app/plugins/panel/histogram/panelcfg.cue b/public/app/plugins/panel/histogram/panelcfg.cue index 27946f13f87..43fc28b3b1e 100644 --- a/public/app/plugins/panel/histogram/panelcfg.cue +++ b/public/app/plugins/panel/histogram/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -27,8 +27,8 @@ composableKinds: PanelCfg: { schemas: [ { PanelOptions: { - ui.OptionsWithLegend - ui.OptionsWithTooltip + common.OptionsWithLegend + common.OptionsWithTooltip //Size of each bucket bucketSize?: int32 @@ -39,8 +39,8 @@ composableKinds: PanelCfg: { } @cuetsy(kind="interface") PanelFieldConfig: { - ui.AxisConfig - ui.HideableFieldConfig + common.AxisConfig + common.HideableFieldConfig // Controls line width of the bars. lineWidth?: uint32 & <=10 | *1 @@ -48,7 +48,7 @@ composableKinds: PanelCfg: { fillOpacity?: uint32 & <=100 | *80 // Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. // Gradient appearance is influenced by the Fill opacity setting. - gradientMode?: ui.GraphGradientMode | *"none" + gradientMode?: common.GraphGradientMode | *"none" } @cuetsy(kind="interface") }, ] diff --git a/public/app/plugins/panel/histogram/panelcfg.gen.ts b/public/app/plugins/panel/histogram/panelcfg.gen.ts index 2b77ef7952d..cb4012cf859 100644 --- a/public/app/plugins/panel/histogram/panelcfg.gen.ts +++ b/public/app/plugins/panel/histogram/panelcfg.gen.ts @@ -8,11 +8,11 @@ // // Run 'make gen-cue' from repository root to regenerate. -import * as ui from '@grafana/schema'; +import * as common from '@grafana/schema'; export const PanelCfgModelVersion = Object.freeze([0, 0]); -export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithTooltip { +export interface PanelOptions extends common.OptionsWithLegend, common.OptionsWithTooltip { /** * Offset buckets by this amount */ @@ -31,7 +31,7 @@ export const defaultPanelOptions: Partial = { bucketOffset: 0, }; -export interface PanelFieldConfig extends ui.AxisConfig, ui.HideableFieldConfig { +export interface PanelFieldConfig extends common.AxisConfig, common.HideableFieldConfig { /** * Controls the fill opacity of the bars. */ @@ -40,7 +40,7 @@ export interface PanelFieldConfig extends ui.AxisConfig, ui.HideableFieldConfig * Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. * Gradient appearance is influenced by the Fill opacity setting. */ - gradientMode?: ui.GraphGradientMode; + gradientMode?: common.GraphGradientMode; /** * Controls line width of the bars. */ @@ -49,6 +49,6 @@ export interface PanelFieldConfig extends ui.AxisConfig, ui.HideableFieldConfig export const defaultPanelFieldConfig: Partial = { fillOpacity: 80, - gradientMode: ui.GraphGradientMode.None, + gradientMode: common.GraphGradientMode.None, lineWidth: 1, }; diff --git a/public/app/plugins/panel/piechart/panelcfg.cue b/public/app/plugins/panel/piechart/panelcfg.cue index 4d5e0e80a87..aaea5b8861a 100644 --- a/public/app/plugins/panel/piechart/panelcfg.cue +++ b/public/app/plugins/panel/piechart/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -39,17 +39,17 @@ composableKinds: PanelCfg: { // - Value: The raw numerical value. PieChartLegendValues: "value" | "percent" @cuetsy(kind="enum") PieChartLegendOptions: { - ui.VizLegendOptions + common.VizLegendOptions values: [...PieChartLegendValues] } @cuetsy(kind="interface") PanelOptions: { - ui.OptionsWithTooltip - ui.SingleStatBaseOptions + common.OptionsWithTooltip + common.SingleStatBaseOptions pieType: PieChartType displayLabels: [...PieChartLabels] legend: PieChartLegendOptions } @cuetsy(kind="interface") - PanelFieldConfig: ui.HideableFieldConfig @cuetsy(kind="interface") + PanelFieldConfig: common.HideableFieldConfig @cuetsy(kind="interface") }, ] }, diff --git a/public/app/plugins/panel/piechart/panelcfg.gen.ts b/public/app/plugins/panel/piechart/panelcfg.gen.ts index db2a2548334..a47ad37c9bd 100644 --- a/public/app/plugins/panel/piechart/panelcfg.gen.ts +++ b/public/app/plugins/panel/piechart/panelcfg.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -import * as ui from '@grafana/schema'; +import * as common from '@grafana/schema'; export const PanelCfgModelVersion = Object.freeze([0, 0]); @@ -42,7 +42,7 @@ export enum PieChartLegendValues { Value = 'value', } -export interface PieChartLegendOptions extends ui.VizLegendOptions { +export interface PieChartLegendOptions extends common.VizLegendOptions { values: Array; } @@ -50,7 +50,7 @@ export const defaultPieChartLegendOptions: Partial = { values: [], }; -export interface PanelOptions extends ui.OptionsWithTooltip, ui.SingleStatBaseOptions { +export interface PanelOptions extends common.OptionsWithTooltip, common.SingleStatBaseOptions { displayLabels: Array; legend: PieChartLegendOptions; pieType: PieChartType; @@ -60,4 +60,4 @@ export const defaultPanelOptions: Partial = { displayLabels: [], }; -export interface PanelFieldConfig extends ui.HideableFieldConfig {} +export interface PanelFieldConfig extends common.HideableFieldConfig {} diff --git a/public/app/plugins/panel/stat/panelcfg.cue b/public/app/plugins/panel/stat/panelcfg.cue index 8b06899418c..4cb87594a86 100644 --- a/public/app/plugins/panel/stat/panelcfg.cue +++ b/public/app/plugins/panel/stat/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -27,11 +27,11 @@ composableKinds: PanelCfg: { schemas: [ { PanelOptions: { - ui.SingleStatBaseOptions - graphMode: ui.BigValueGraphMode | *"area" - colorMode: ui.BigValueColorMode | *"value" - justifyMode: ui.BigValueJustifyMode | *"auto" - textMode: ui.BigValueTextMode | *"auto" + common.SingleStatBaseOptions + graphMode: common.BigValueGraphMode | *"area" + colorMode: common.BigValueColorMode | *"value" + justifyMode: common.BigValueJustifyMode | *"auto" + textMode: common.BigValueTextMode | *"auto" } @cuetsy(kind="interface") }, ] diff --git a/public/app/plugins/panel/stat/panelcfg.gen.ts b/public/app/plugins/panel/stat/panelcfg.gen.ts index 11a22602500..c1a246e82e3 100644 --- a/public/app/plugins/panel/stat/panelcfg.gen.ts +++ b/public/app/plugins/panel/stat/panelcfg.gen.ts @@ -8,20 +8,20 @@ // // Run 'make gen-cue' from repository root to regenerate. -import * as ui from '@grafana/schema'; +import * as common from '@grafana/schema'; export const PanelCfgModelVersion = Object.freeze([0, 0]); -export interface PanelOptions extends ui.SingleStatBaseOptions { - colorMode: ui.BigValueColorMode; - graphMode: ui.BigValueGraphMode; - justifyMode: ui.BigValueJustifyMode; - textMode: ui.BigValueTextMode; +export interface PanelOptions extends common.SingleStatBaseOptions { + colorMode: common.BigValueColorMode; + graphMode: common.BigValueGraphMode; + justifyMode: common.BigValueJustifyMode; + textMode: common.BigValueTextMode; } export const defaultPanelOptions: Partial = { - colorMode: ui.BigValueColorMode.Value, - graphMode: ui.BigValueGraphMode.Area, - justifyMode: ui.BigValueJustifyMode.Auto, - textMode: ui.BigValueTextMode.Auto, + colorMode: common.BigValueColorMode.Value, + graphMode: common.BigValueGraphMode.Area, + justifyMode: common.BigValueJustifyMode.Auto, + textMode: common.BigValueTextMode.Auto, }; diff --git a/public/app/plugins/panel/state-timeline/panelcfg.cue b/public/app/plugins/panel/state-timeline/panelcfg.cue index 6ee5b04192c..dd0a822f463 100644 --- a/public/app/plugins/panel/state-timeline/panelcfg.cue +++ b/public/app/plugins/panel/state-timeline/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -29,17 +29,17 @@ composableKinds: PanelCfg: { PanelOptions: { // FIXME ts comments indicate this shouldn't be in the saved model, but currently is emitted mode?: TimelineMode - ui.OptionsWithLegend - ui.OptionsWithTooltip - ui.OptionsWithTimezones - showValue: ui.VisibilityMode | *"auto" + common.OptionsWithLegend + common.OptionsWithTooltip + common.OptionsWithTimezones + showValue: common.VisibilityMode | *"auto" rowHeight: number | *0.9 colWidth?: number mergeValues?: bool | *true alignValue?: TimelineValueAlignment | *"left" } @cuetsy(kind="interface") PanelFieldConfig: { - ui.HideableFieldConfig + common.HideableFieldConfig lineWidth?: number | *0 fillOpacity?: number | *70 } @cuetsy(kind="interface") diff --git a/public/app/plugins/panel/status-history/panelcfg.cue b/public/app/plugins/panel/status-history/panelcfg.cue index 74a31517efa..b16499ad837 100644 --- a/public/app/plugins/panel/status-history/panelcfg.cue +++ b/public/app/plugins/panel/status-history/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -25,16 +25,16 @@ composableKinds: PanelCfg: { schemas: [ { PanelOptions: { - ui.OptionsWithLegend - ui.OptionsWithTooltip - ui.OptionsWithTimezones - showValue: ui.VisibilityMode + common.OptionsWithLegend + common.OptionsWithTooltip + common.OptionsWithTimezones + showValue: common.VisibilityMode rowHeight: number colWidth?: number alignValue: "center" | *"left" | "right" } @cuetsy(kind="interface") PanelFieldConfig: { - ui.HideableFieldConfig + common.HideableFieldConfig lineWidth?: number | *1 fillOpacity?: number | *70 } @cuetsy(kind="interface") diff --git a/public/app/plugins/panel/table/panelcfg.cue b/public/app/plugins/panel/table/panelcfg.cue index 822fcabafe0..3b49cc6b87b 100644 --- a/public/app/plugins/panel/table/panelcfg.cue +++ b/public/app/plugins/panel/table/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -28,9 +28,9 @@ composableKinds: PanelCfg: { frameIndex: number | *0 showHeader: bool | *true showTypeIcons: bool | *false - sortBy?: [...ui.TableSortByFieldState] + sortBy?: [...common.TableSortByFieldState] } @cuetsy(kind="interface") - PanelFieldConfig: ui.TableFieldOptions & {} @cuetsy(kind="interface") + PanelFieldConfig: common.TableFieldOptions & {} @cuetsy(kind="interface") }, ] }, diff --git a/public/app/plugins/panel/timeseries/panelcfg.cue b/public/app/plugins/panel/timeseries/panelcfg.cue index 963a4a13424..da7e4a8d01c 100644 --- a/public/app/plugins/panel/timeseries/panelcfg.cue +++ b/public/app/plugins/panel/timeseries/panelcfg.cue @@ -15,7 +15,7 @@ package grafanaplugin import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { @@ -25,10 +25,10 @@ composableKinds: PanelCfg: { schemas: [ { PanelOptions: { - legend: ui.VizLegendOptions - tooltip: ui.VizTooltipOptions + legend: common.VizLegendOptions + tooltip: common.VizTooltipOptions } @cuetsy(kind="interface") - PanelFieldConfig: ui.GraphFieldConfig & {} @cuetsy(kind="interface") + PanelFieldConfig: common.GraphFieldConfig & {} @cuetsy(kind="interface") }, ] }, From fe27acc3a924650ad1c97aa81a7f6c5854e9bde6 Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Mon, 23 Jan 2023 15:10:14 -0500 Subject: [PATCH 09/46] chore: move validations model into the validations service (#61953) --- pkg/api/http_server.go | 31 +++++++++---------- pkg/cmd/grafana-cli/runner/wireexts_oss.go | 3 +- .../host_redirect_validation_middleware.go | 6 ++-- .../http_client_provider.go | 7 +++-- pkg/server/wireexts_oss.go | 3 +- pkg/services/alerting/engine.go | 6 ++-- pkg/services/alerting/eval_context.go | 6 ++-- .../datasourceproxy/datasourceproxy.go | 5 +-- pkg/services/query/query.go | 9 +++--- .../validations/service.go} | 2 +- 10 files changed, 39 insertions(+), 39 deletions(-) rename pkg/{models/validations.go => services/validations/service.go} (92%) diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 7dd28a533e5..2a90a6f04a2 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -13,25 +13,13 @@ import ( "strings" "sync" - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/middleware/csrf" - "github.com/grafana/grafana/pkg/services/auth" - "github.com/grafana/grafana/pkg/services/authn" - "github.com/grafana/grafana/pkg/services/folder" - "github.com/grafana/grafana/pkg/services/licensing" - "github.com/grafana/grafana/pkg/services/oauthtoken" - "github.com/grafana/grafana/pkg/services/querylibrary" - "github.com/grafana/grafana/pkg/services/searchV2" - "github.com/grafana/grafana/pkg/services/stats" - "github.com/grafana/grafana/pkg/services/store/entity/httpentitystore" - "github.com/grafana/grafana/pkg/services/store/k8saccess" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/grafana/grafana/pkg/api/avatar" "github.com/grafana/grafana/pkg/api/routing" httpstatic "github.com/grafana/grafana/pkg/api/static" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/kvstore" @@ -42,7 +30,7 @@ import ( loginpkg "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/middleware" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/middleware/csrf" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/plugincontext" "github.com/grafana/grafana/pkg/registry/corekind" @@ -50,6 +38,8 @@ import ( "github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/apikey" + "github.com/grafana/grafana/pkg/services/auth" + "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/cleanup" "github.com/grafana/grafana/pkg/services/comments" "github.com/grafana/grafana/pkg/services/contexthandler" @@ -63,10 +53,12 @@ import ( "github.com/grafana/grafana/pkg/services/encryption" "github.com/grafana/grafana/pkg/services/export" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/hooks" "github.com/grafana/grafana/pkg/services/ldap" "github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/librarypanels" + "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/live" "github.com/grafana/grafana/pkg/services/live/pushhttp" "github.com/grafana/grafana/pkg/services/login" @@ -74,6 +66,7 @@ import ( "github.com/grafana/grafana/pkg/services/navtree" "github.com/grafana/grafana/pkg/services/ngalert" "github.com/grafana/grafana/pkg/services/notifications" + "github.com/grafana/grafana/pkg/services/oauthtoken" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/playlist" "github.com/grafana/grafana/pkg/services/plugindashboards" @@ -83,9 +76,11 @@ import ( publicdashboardsApi "github.com/grafana/grafana/pkg/services/publicdashboards/api" "github.com/grafana/grafana/pkg/services/query" "github.com/grafana/grafana/pkg/services/queryhistory" + "github.com/grafana/grafana/pkg/services/querylibrary" "github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/search" + "github.com/grafana/grafana/pkg/services/searchV2" "github.com/grafana/grafana/pkg/services/searchusers" "github.com/grafana/grafana/pkg/services/secrets" secretsKV "github.com/grafana/grafana/pkg/services/secrets/kvstore" @@ -94,7 +89,10 @@ import ( "github.com/grafana/grafana/pkg/services/shorturls" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/star" + "github.com/grafana/grafana/pkg/services/stats" "github.com/grafana/grafana/pkg/services/store" + "github.com/grafana/grafana/pkg/services/store/entity/httpentitystore" + "github.com/grafana/grafana/pkg/services/store/k8saccess" "github.com/grafana/grafana/pkg/services/tag" "github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/teamguardian" @@ -102,6 +100,7 @@ import ( "github.com/grafana/grafana/pkg/services/thumbs" "github.com/grafana/grafana/pkg/services/updatechecker" "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/web" ) @@ -133,7 +132,7 @@ type HTTPServer struct { License licensing.Licensing AccessControl accesscontrol.AccessControl DataProxy *datasourceproxy.DataSourceProxyService - PluginRequestValidator models.PluginRequestValidator + PluginRequestValidator validations.PluginRequestValidator pluginClient plugins.Client pluginStore plugins.Store pluginInstaller plugins.Installer @@ -223,7 +222,7 @@ type ServerOptions struct { func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus, renderService rendering.Service, licensing licensing.Licensing, hooksService *hooks.HooksService, cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore, alertEngine *alerting.AlertEngine, - pluginRequestValidator models.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver, + pluginRequestValidator validations.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver, pluginDashboardService plugindashboards.Service, pluginStore plugins.Store, pluginClient plugins.Client, pluginErrorResolver plugins.ErrorResolver, pluginInstaller plugins.Installer, settingsProvider setting.Provider, dataSourceCache datasources.CacheService, userTokenService auth.UserTokenService, diff --git a/pkg/cmd/grafana-cli/runner/wireexts_oss.go b/pkg/cmd/grafana-cli/runner/wireexts_oss.go index 313cafad088..be33d32cd94 100644 --- a/pkg/cmd/grafana-cli/runner/wireexts_oss.go +++ b/pkg/cmd/grafana-cli/runner/wireexts_oss.go @@ -6,7 +6,6 @@ package runner import ( "github.com/google/wire" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/server/backgroundsvcs" "github.com/grafana/grafana/pkg/server/usagestatssvcs" @@ -56,7 +55,7 @@ var wireExtsSet = wire.NewSet( thumbs.ProvideCrawlerAuthSetupService, wire.Bind(new(thumbs.CrawlerAuthSetupService), new(*thumbs.OSSCrawlerAuthSetupService)), validations.ProvideValidator, - wire.Bind(new(models.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)), + wire.Bind(new(validations.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)), provisioning.ProvideService, wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)), backgroundsvcs.ProvideBackgroundServiceRegistry, diff --git a/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go b/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go index 7bffa1126c6..c53ead6e7a5 100644 --- a/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go +++ b/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go @@ -4,14 +4,14 @@ import ( "errors" "net/http" - "github.com/grafana/grafana/pkg/models" - sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + + "github.com/grafana/grafana/pkg/services/validations" ) const HostRedirectValidationMiddlewareName = "host-redirect-validation" -func RedirectLimitMiddleware(reqValidator models.PluginRequestValidator) sdkhttpclient.Middleware { +func RedirectLimitMiddleware(reqValidator validations.PluginRequestValidator) sdkhttpclient.Middleware { return sdkhttpclient.NamedMiddlewareFunc(HostRedirectValidationMiddlewareName, func(opts sdkhttpclient.Options, next http.RoundTripper) http.RoundTripper { return sdkhttpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { res, err := next.RoundTrip(req) diff --git a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go index da9eb67f966..c8aea67c8a3 100644 --- a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go +++ b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go @@ -6,19 +6,20 @@ import ( "time" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/mwitkow/go-conntrack" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics/metricutil" "github.com/grafana/grafana/pkg/infra/tracing" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" - "github.com/mwitkow/go-conntrack" ) var newProviderFunc = sdkhttpclient.NewProvider // New creates a new HTTP client provider with pre-configured middlewares. -func New(cfg *setting.Cfg, validator models.PluginRequestValidator, tracer tracing.Tracer) *sdkhttpclient.Provider { +func New(cfg *setting.Cfg, validator validations.PluginRequestValidator, tracer tracing.Tracer) *sdkhttpclient.Provider { logger := log.New("httpclient") userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion) diff --git a/pkg/server/wireexts_oss.go b/pkg/server/wireexts_oss.go index a8ea8aa44de..6ce8a35c7f4 100644 --- a/pkg/server/wireexts_oss.go +++ b/pkg/server/wireexts_oss.go @@ -6,7 +6,6 @@ package server import ( "github.com/google/wire" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/server/backgroundsvcs" @@ -53,7 +52,7 @@ var wireExtsBasicSet = wire.NewSet( thumbs.ProvideCrawlerAuthSetupService, wire.Bind(new(thumbs.CrawlerAuthSetupService), new(*thumbs.OSSCrawlerAuthSetupService)), validations.ProvideValidator, - wire.Bind(new(models.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)), + wire.Bind(new(validations.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)), provisioning.ProvideService, wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)), backgroundsvcs.ProvideBackgroundServiceRegistry, diff --git a/pkg/services/alerting/engine.go b/pkg/services/alerting/engine.go index 316b3771305..c20c821bb0d 100644 --- a/pkg/services/alerting/engine.go +++ b/pkg/services/alerting/engine.go @@ -15,13 +15,13 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/usagestats" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/encryption" "github.com/grafana/grafana/pkg/services/notifications" "github.com/grafana/grafana/pkg/services/rendering" + "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/legacydata" "github.com/grafana/grafana/pkg/util/ticker" @@ -32,7 +32,7 @@ import ( // are sent. type AlertEngine struct { RenderService rendering.Service - RequestValidator models.PluginRequestValidator + RequestValidator validations.PluginRequestValidator DataService legacydata.RequestHandler Cfg *setting.Cfg @@ -58,7 +58,7 @@ func (e *AlertEngine) IsDisabled() bool { } // ProvideAlertEngine returns a new AlertEngine. -func ProvideAlertEngine(renderer rendering.Service, requestValidator models.PluginRequestValidator, +func ProvideAlertEngine(renderer rendering.Service, requestValidator validations.PluginRequestValidator, dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal, notificationService *notifications.NotificationService, tracer tracing.Tracer, store AlertStore, cfg *setting.Cfg, dashAlertExtractor DashAlertExtractor, dashboardService dashboards.DashboardService, cacheService *localcache.CacheService, dsService datasources.DataSourceService, annotationsRepo annotations.Repository) *AlertEngine { diff --git a/pkg/services/alerting/eval_context.go b/pkg/services/alerting/eval_context.go index dff04f79e59..d5375cae94d 100644 --- a/pkg/services/alerting/eval_context.go +++ b/pkg/services/alerting/eval_context.go @@ -7,11 +7,11 @@ import ( "time" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" alertmodels "github.com/grafana/grafana/pkg/services/alerting/models" "github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" ) @@ -37,7 +37,7 @@ type EvalContext struct { NoDataFound bool PrevAlertState alertmodels.AlertStateType - RequestValidator models.PluginRequestValidator + RequestValidator validations.PluginRequestValidator Ctx context.Context @@ -48,7 +48,7 @@ type EvalContext struct { } // NewEvalContext is the EvalContext constructor. -func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator models.PluginRequestValidator, +func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator validations.PluginRequestValidator, alertStore AlertStore, dashboardService dashboards.DashboardService, dsService datasources.DataSourceService, annotationRepo annotations.Repository) *EvalContext { return &EvalContext{ Ctx: alertCtx, diff --git a/pkg/services/datasourceproxy/datasourceproxy.go b/pkg/services/datasourceproxy/datasourceproxy.go index 0590376c741..81a2f5fdb1c 100644 --- a/pkg/services/datasourceproxy/datasourceproxy.go +++ b/pkg/services/datasourceproxy/datasourceproxy.go @@ -17,12 +17,13 @@ import ( "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/oauthtoken" "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/web" ) -func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator models.PluginRequestValidator, +func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator validations.PluginRequestValidator, pluginStore plugins.Store, cfg *setting.Cfg, httpClientProvider httpclient.Provider, oauthTokenService *oauthtoken.Service, dsService datasources.DataSourceService, tracer tracing.Tracer, secretsService secrets.Service) *DataSourceProxyService { @@ -41,7 +42,7 @@ func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator m type DataSourceProxyService struct { DataSourceCache datasources.CacheService - PluginRequestValidator models.PluginRequestValidator + PluginRequestValidator validations.PluginRequestValidator pluginStore plugins.Store Cfg *setting.Cfg HTTPClientProvider httpclient.Provider diff --git a/pkg/services/query/query.go b/pkg/services/query/query.go index 2c0f730c0a9..9befc139afb 100644 --- a/pkg/services/query/query.go +++ b/pkg/services/query/query.go @@ -6,20 +6,21 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend" + "golang.org/x/sync/errgroup" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/adapters" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/grafanads" "github.com/grafana/grafana/pkg/tsdb/legacydata" "github.com/grafana/grafana/pkg/util/errutil" - "golang.org/x/sync/errgroup" ) const ( @@ -33,7 +34,7 @@ func ProvideService( cfg *setting.Cfg, dataSourceCache datasources.CacheService, expressionService *expr.Service, - pluginRequestValidator models.PluginRequestValidator, + pluginRequestValidator validations.PluginRequestValidator, dataSourceService datasources.DataSourceService, pluginClient plugins.Client, ) *Service { @@ -54,7 +55,7 @@ type Service struct { cfg *setting.Cfg dataSourceCache datasources.CacheService expressionService *expr.Service - pluginRequestValidator models.PluginRequestValidator + pluginRequestValidator validations.PluginRequestValidator dataSourceService datasources.DataSourceService pluginClient plugins.Client log log.Logger diff --git a/pkg/models/validations.go b/pkg/services/validations/service.go similarity index 92% rename from pkg/models/validations.go rename to pkg/services/validations/service.go index 389e5466f80..9b0d48a8f3d 100644 --- a/pkg/models/validations.go +++ b/pkg/services/validations/service.go @@ -1,4 +1,4 @@ -package models +package validations import ( "net/http" From 9b5e396be287e7a2cc87966d0556910d614336a7 Mon Sep 17 00:00:00 2001 From: juanicabanas Date: Mon, 23 Jan 2023 17:23:23 -0300 Subject: [PATCH 10/46] PublicDashboards: Checkboxes list refactor (#61947) --- .../AcknowledgeCheckboxes.tsx | 112 +++++++++--------- .../SharePublicDashboard.tsx | 2 +- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/AcknowledgeCheckboxes.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/AcknowledgeCheckboxes.tsx index 21c178b987c..3d010865c96 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/AcknowledgeCheckboxes.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/AcknowledgeCheckboxes.tsx @@ -4,7 +4,48 @@ import { UseFormRegister } from 'react-hook-form'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { Checkbox, FieldSet, HorizontalGroup, LinkButton, VerticalGroup } from '@grafana/ui/src'; -import { SharePublicDashboardInputs } from './SharePublicDashboard'; +import { SharePublicDashboardAcknowledgmentInputs, SharePublicDashboardInputs } from './SharePublicDashboard'; + +type Acknowledge = { + type: keyof SharePublicDashboardAcknowledgmentInputs; + description: string; + testId: string; + info: { + href: string; + tooltip: string; + }; +}; +const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; + +const ACKNOWLEDGES: Acknowledge[] = [ + { + type: 'publicAcknowledgment', + description: 'Your entire dashboard will be public', + testId: selectors.WillBePublicCheckbox, + info: { + href: 'https://grafana.com/docs/grafana/latest/dashboards/dashboard-public/', + tooltip: 'Learn more about public dashboards', + }, + }, + { + type: 'dataSourcesAcknowledgment', + description: 'Publishing currently only works with a subset of datasources', + testId: selectors.LimitedDSCheckbox, + info: { + href: 'https://grafana.com/docs/grafana/latest/datasources/', + tooltip: 'Learn more about public datasources', + }, + }, + { + type: 'usageAcknowledgment', + description: 'Making a dashboard public causes queries to run each time it is viewed, which may increase costs', + testId: selectors.CostIncreaseCheckbox, + info: { + href: 'https://grafana.com/docs/grafana/latest/enterprise/query-caching/', + tooltip: 'Learn more about query caching', + }, + }, +]; export const AcknowledgeCheckboxes = ({ disabled, @@ -12,65 +53,30 @@ export const AcknowledgeCheckboxes = ({ }: { disabled: boolean; register: UseFormRegister; -}) => { - const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; - - return ( - <> -

Before you click Save, please acknowledge the following information:

-
- - +}) => ( + <> +

Before you click Save, please acknowledge the following information:

+
+ + {ACKNOWLEDGES.map((acknowledge) => ( + - - - - - - - - - - -
- - ); -}; + ))} +
+
+ +); diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx index 30ce9afe4e7..270671fe812 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx @@ -45,7 +45,7 @@ import { ShareModal } from '../ShareModal'; interface Props extends ShareModalTabProps {} -type SharePublicDashboardAcknowledgmentInputs = { +export type SharePublicDashboardAcknowledgmentInputs = { publicAcknowledgment: boolean; dataSourcesAcknowledgment: boolean; usageAcknowledgment: boolean; From 949857f3b1140fe19141227e3881e07655676c6e Mon Sep 17 00:00:00 2001 From: juanicabanas Date: Mon, 23 Jan 2023 17:30:20 -0300 Subject: [PATCH 11/46] PublicDashboards: Footer position fix (#61954) --- .../app/features/dashboard/containers/PublicDashboardPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/containers/PublicDashboardPage.tsx b/public/app/features/dashboard/containers/PublicDashboardPage.tsx index 531f779caa0..9c0bc278815 100644 --- a/public/app/features/dashboard/containers/PublicDashboardPage.tsx +++ b/public/app/features/dashboard/containers/PublicDashboardPage.tsx @@ -97,7 +97,7 @@ const PublicDashboardPage = (props: Props) => { layout={PageLayoutType.Custom} toolbar={} > - {dashboardState.initError && } + {dashboardState.initError && }
@@ -108,6 +108,7 @@ const PublicDashboardPage = (props: Props) => { const getStyles = (theme: GrafanaTheme2) => ({ gridContainer: css({ + flex: 1, padding: theme.spacing(0, 2, 2, 2), overflow: 'auto', }), From 3146740d821a8dd69bb4cf29885805b19a48f985 Mon Sep 17 00:00:00 2001 From: jeremybanzhaf <88380596+jeremybanzhaf@users.noreply.github.com> Date: Mon, 23 Jan 2023 22:57:11 +0100 Subject: [PATCH 12/46] Docs: Update index.md to not escape dollar sign (#61694) Update index.md to not escape dollar sign the backslash is useful in json files, but not where most users will see it --- docs/sources/datasources/prometheus/template-variables/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/datasources/prometheus/template-variables/index.md b/docs/sources/datasources/prometheus/template-variables/index.md index 6e7fde54116..8b69205c235 100644 --- a/docs/sources/datasources/prometheus/template-variables/index.md +++ b/docs/sources/datasources/prometheus/template-variables/index.md @@ -99,7 +99,7 @@ For details, refer to the [Grafana blog](/blog/2020/09/28/new-in-grafana-7.2-__r The Prometheus data source supports two variable syntaxes for use in the **Query** field: -- `$`, for example `rate(http_requests_total{job=~"\$job"}[$_rate_interval])`, which is easier to read and write but does not allow you to use a variable in the middle of a word. +- `$`, for example `rate(http_requests_total{job=~"$job"}[$_rate_interval])`, which is easier to read and write but does not allow you to use a variable in the middle of a word. - `[[varname]]`, for example `rate(http_requests_total{job=~"[[job]]"}[$_rate_interval])` If you've enabled the _Multi-value_ or _Include all value_ options, Grafana converts the labels from plain text to a regex-compatible string, which requires you to use `=~` instead of `=`. From 7ccc8451878314728a4b29ce7b9c5494efb67ddf Mon Sep 17 00:00:00 2001 From: Alexander Weaver Date: Mon, 23 Jan 2023 16:31:03 -0600 Subject: [PATCH 13/46] Alerting: Push state history entries to Loki (#61724) * Implement push endpoint * Drop duplicated struct * Genericize auth/tenant headers and improve logging in error case * Flesh out the data model * Drop dead code * Drop log line entirely * Drop unused arg * Rename a few type manipulation functions * Extract label keys as constants * Improve logs when loki responds with error * Inline lokiRepresentation function --- pkg/services/ngalert/state/historian/loki.go | 113 +++++++++++++++++- .../ngalert/state/historian/loki_http.go | 81 +++++++++++-- .../ngalert/state/historian/loki_http_test.go | 34 +++--- 3 files changed, 201 insertions(+), 27 deletions(-) diff --git a/pkg/services/ngalert/state/historian/loki.go b/pkg/services/ngalert/state/historian/loki.go index e18b58d3305..4b1c64c41bd 100644 --- a/pkg/services/ngalert/state/historian/loki.go +++ b/pkg/services/ngalert/state/historian/loki.go @@ -2,15 +2,28 @@ package historian import ( "context" + "encoding/json" + "fmt" + "sort" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/state" ) +const ( + OrgIDLabel = "orgID" + RuleUIDLabel = "ruleUID" + GroupLabel = "group" + FolderUIDLabel = "folderUID" +) + type remoteLokiClient interface { ping() error + push([]stream) error } type RemoteLokiBackend struct { @@ -30,11 +43,107 @@ func (h *RemoteLokiBackend) TestConnection() error { return h.client.ping() } -func (h *RemoteLokiBackend) RecordStatesAsync(ctx context.Context, _ *models.AlertRule, _ []state.StateTransition) { +func (h *RemoteLokiBackend) RecordStatesAsync(ctx context.Context, rule *models.AlertRule, states []state.StateTransition) { logger := h.log.FromContext(ctx) - logger.Debug("Remote Loki state history backend was called with states") + streams := h.statesToStreams(rule, states, logger) + h.recordStreamsAsync(ctx, streams, logger) } func (h *RemoteLokiBackend) QueryStates(ctx context.Context, query models.HistoryQuery) (*data.Frame, error) { return data.NewFrame("states"), nil } + +func (h *RemoteLokiBackend) statesToStreams(rule *models.AlertRule, states []state.StateTransition, logger log.Logger) []stream { + buckets := make(map[string][]row) // label repr -> entries + for _, state := range states { + if !shouldRecord(state) { + continue + } + + labels := removePrivateLabels(state.State.Labels) + labels[OrgIDLabel] = fmt.Sprint(rule.OrgID) + labels[RuleUIDLabel] = fmt.Sprint(rule.UID) + labels[GroupLabel] = fmt.Sprint(rule.RuleGroup) + labels[FolderUIDLabel] = fmt.Sprint(rule.NamespaceUID) + repr := labels.String() + + entry := lokiEntry{ + SchemaVersion: 1, + Previous: state.PreviousFormatted(), + Current: state.Formatted(), + Values: valuesAsDataBlob(state.State), + } + jsn, err := json.Marshal(entry) + if err != nil { + logger.Error("Failed to construct history record for state, skipping", "error", err) + continue + } + line := string(jsn) + + buckets[repr] = append(buckets[repr], row{ + At: state.State.LastEvaluationTime, + Val: line, + }) + } + + result := make([]stream, 0, len(buckets)) + for repr, rows := range buckets { + labels, err := data.LabelsFromString(repr) + if err != nil { + logger.Error("Failed to parse frame labels, skipping state history batch: %w", err) + continue + } + result = append(result, stream{ + Stream: labels, + Values: rows, + }) + } + + return result +} + +func (h *RemoteLokiBackend) recordStreamsAsync(ctx context.Context, streams []stream, logger log.Logger) { + go func() { + if err := h.recordStreams(ctx, streams, logger); err != nil { + logger.Error("Failed to save alert state history batch", "error", err) + } + }() +} + +func (h *RemoteLokiBackend) recordStreams(ctx context.Context, streams []stream, logger log.Logger) error { + if err := h.client.push(streams); err != nil { + return err + } + logger.Debug("Done saving alert state history batch") + return nil +} + +type lokiEntry struct { + SchemaVersion int `json:"schemaVersion"` + Previous string `json:"previous"` + Current string `json:"current"` + Values *simplejson.Json `json:"values"` +} + +func valuesAsDataBlob(state *state.State) *simplejson.Json { + jsonData := simplejson.New() + + switch state.State { + case eval.Error: + if state.Error == nil { + jsonData.Set("error", nil) + } else { + jsonData.Set("error", state.Error.Error()) + } + case eval.NoData: + jsonData.Set("noData", true) + default: + keys := make([]string, 0, len(state.Values)) + for k := range state.Values { + keys = append(keys, k) + } + sort.Strings(keys) + jsonData.Set("values", simplejson.NewFromAny(state.Values)) + } + return jsonData +} diff --git a/pkg/services/ngalert/state/historian/loki_http.go b/pkg/services/ngalert/state/historian/loki_http.go index bea1693b907..457516b0b4f 100644 --- a/pkg/services/ngalert/state/historian/loki_http.go +++ b/pkg/services/ngalert/state/historian/loki_http.go @@ -1,7 +1,10 @@ package historian import ( + "bytes" + "encoding/json" "fmt" + "io" "net/http" "net/url" "time" @@ -37,18 +40,10 @@ func newLokiClient(cfg LokiConfig, logger log.Logger) *httpLokiClient { func (c *httpLokiClient) ping() error { uri := c.cfg.Url.JoinPath("/loki/api/v1/labels") req, err := http.NewRequest(http.MethodGet, uri.String(), nil) - - if c.cfg.BasicAuthUser != "" || c.cfg.BasicAuthPassword != "" { - req.SetBasicAuth(c.cfg.BasicAuthUser, c.cfg.BasicAuthPassword) - } - - if c.cfg.TenantID != "" { - req.Header.Add("X-Scope-OrgID", c.cfg.TenantID) - } - if err != nil { return fmt.Errorf("error creating request: %w", err) } + c.setAuthAndTenantHeaders(req) res, err := c.client.Do(req) if res != nil { @@ -68,3 +63,71 @@ func (c *httpLokiClient) ping() error { c.log.Debug("Ping request to Loki endpoint succeeded", "status", res.StatusCode) return nil } + +type stream struct { + Stream map[string]string `json:"stream"` + Values []row `json:"values"` +} + +type row struct { + At time.Time + Val string +} + +func (r *row) MarshalJSON() ([]byte, error) { + return json.Marshal([2]string{ + fmt.Sprintf("%d", r.At.UnixNano()), r.Val, + }) +} + +func (c *httpLokiClient) push(s []stream) error { + body := struct { + Streams []stream `json:"streams"` + }{Streams: s} + enc, err := json.Marshal(body) + if err != nil { + return fmt.Errorf("failed to serialize Loki payload: %w", err) + } + + uri := c.cfg.Url.JoinPath("/loki/api/v1/push") + req, err := http.NewRequest(http.MethodPost, uri.String(), bytes.NewBuffer(enc)) + if err != nil { + return fmt.Errorf("failed to create Loki request: %w", err) + } + + c.setAuthAndTenantHeaders(req) + req.Header.Add("content-type", "application/json") + + resp, err := c.client.Do(req) + if resp != nil { + defer func() { + if err := resp.Body.Close(); err != nil { + c.log.Warn("Failed to close response body", "err", err) + } + }() + } + if err != nil { + return fmt.Errorf("failed to send request: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + byt, _ := io.ReadAll(resp.Body) + if len(byt) > 0 { + c.log.Error("Error response from Loki", "response", string(byt), "status", resp.StatusCode) + } else { + c.log.Error("Error response from Loki with an empty body", "status", resp.StatusCode) + } + return fmt.Errorf("received a non-200 response from loki, status: %d", resp.StatusCode) + } + return nil +} + +func (c *httpLokiClient) setAuthAndTenantHeaders(req *http.Request) { + if c.cfg.BasicAuthUser != "" || c.cfg.BasicAuthPassword != "" { + req.SetBasicAuth(c.cfg.BasicAuthUser, c.cfg.BasicAuthPassword) + } + + if c.cfg.TenantID != "" { + req.Header.Add("X-Scope-OrgID", c.cfg.TenantID) + } +} diff --git a/pkg/services/ngalert/state/historian/loki_http_test.go b/pkg/services/ngalert/state/historian/loki_http_test.go index 4a76b2e65f4..89947ccd8fc 100644 --- a/pkg/services/ngalert/state/historian/loki_http_test.go +++ b/pkg/services/ngalert/state/historian/loki_http_test.go @@ -12,25 +12,27 @@ import ( func TestLokiHTTPClient(t *testing.T) { t.Skip() - url, err := url.Parse("https://logs-prod-eu-west-0.grafana.net") - require.NoError(t, err) + t.Run("smoke test pinging Loki", func(t *testing.T) { + url, err := url.Parse("https://logs-prod-eu-west-0.grafana.net") + require.NoError(t, err) - client := newLokiClient(LokiConfig{ - Url: url, - }, log.NewNopLogger()) + client := newLokiClient(LokiConfig{ + Url: url, + }, log.NewNopLogger()) - // Unauthorized request should fail against Grafana Cloud. - err = client.ping() - require.Error(t, err) + // Unauthorized request should fail against Grafana Cloud. + err = client.ping() + require.Error(t, err) - client.cfg.BasicAuthUser = "" - client.cfg.BasicAuthPassword = "" + client.cfg.BasicAuthUser = "" + client.cfg.BasicAuthPassword = "" - // When running on prem, you might need to set the tenant id, - // so the x-scope-orgid header is set. - // client.cfg.TenantID = "" + // When running on prem, you might need to set the tenant id, + // so the x-scope-orgid header is set. + // client.cfg.TenantID = "" - // Authorized request should fail against Grafana Cloud. - err = client.ping() - require.NoError(t, err) + // Authorized request should fail against Grafana Cloud. + err = client.ping() + require.NoError(t, err) + }) } From 3006a457f2ca62369149fb24de02c757bb6bec9a Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Tue, 24 Jan 2023 01:12:56 +0200 Subject: [PATCH 14/46] Geomap panel: Generate types (#61636) --- .betterer.results | 14 +-- kinds/dashboard/dashboard_kind.cue | 2 +- packages/grafana-data/src/geo/layer.ts | 67 ++------------ .../grafana-data/src/types/transformations.ts | 5 +- .../grafana-schema/src/common/common.gen.ts | 53 +++++++++++ packages/grafana-schema/src/common/geo.cue | 33 +++++++ .../grafana-schema/src/common/mudball.cue | 2 - packages/grafana-schema/src/index.gen.ts | 4 +- .../grafana-schema/src/veneer/common.types.ts | 8 ++ .../src/veneer/dashboard.types.ts | 5 ++ .../app/features/geo/editor/locationEditor.ts | 10 +-- .../geo/editor/locationModeEditor.tsx | 9 +- .../app/features/geo/utils/location.test.ts | 3 +- public/app/features/geo/utils/location.ts | 3 +- .../spatial/SpatialTransformerEditor.tsx | 3 +- .../transformers/spatial/models.gen.ts | 2 +- .../app/plugins/panel/geomap/GeomapPanel.tsx | 6 +- .../panel/geomap/editor/FitMapViewEditor.tsx | 4 +- .../panel/geomap/editor/LayersEditor.tsx | 4 +- .../panel/geomap/editor/MapViewEditor.tsx | 4 +- .../panel/geomap/layers/data/photosLayer.tsx | 3 +- .../panel/geomap/layers/data/routeLayer.tsx | 8 +- public/app/plugins/panel/geomap/migrations.ts | 8 +- public/app/plugins/panel/geomap/models.cue | 76 ++++++++++++++++ public/app/plugins/panel/geomap/models.gen.ts | 89 +++++++++++++++++++ public/app/plugins/panel/geomap/module.tsx | 8 +- public/app/plugins/panel/geomap/types.ts | 68 +------------- .../app/plugins/panel/geomap/utils/actions.ts | 2 +- .../app/plugins/panel/geomap/utils/utils.ts | 6 +- 29 files changed, 325 insertions(+), 184 deletions(-) create mode 100644 packages/grafana-schema/src/common/geo.cue create mode 100644 public/app/plugins/panel/geomap/models.cue create mode 100644 public/app/plugins/panel/geomap/models.gen.ts diff --git a/.betterer.results b/.betterer.results index d7f730b52c0..c52010d3415 100644 --- a/.betterer.results +++ b/.betterer.results @@ -277,8 +277,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "packages/grafana-data/src/geo/layer.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "packages/grafana-data/src/panel/PanelPlugin.test.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -584,8 +583,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"] + [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], "packages/grafana-data/src/types/variables.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -993,10 +991,14 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Do not use any type assertions.", "4"] ], + "packages/grafana-schema/src/veneer/common.types.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "packages/grafana-schema/src/veneer/dashboard.types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"] ], "packages/grafana-toolkit/src/cli/tasks/component.create.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 62a905c3dea..d792acc924a 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -413,7 +413,7 @@ lineage: seqs: [ #MatcherConfig: { id: string | *"" @grafanamaturity(NeedsExpertReview) options?: _ @grafanamaturity(NeedsExpertReview) - } @cuetsy(kind="interface") + } @cuetsy(kind="interface") @grafana(TSVeneer="type") #DynamicConfigValue: { id: string | *"" @grafanamaturity(NeedsExpertReview) diff --git a/packages/grafana-data/src/geo/layer.ts b/packages/grafana-data/src/geo/layer.ts index 6859174ef7c..4ad67866fbb 100644 --- a/packages/grafana-data/src/geo/layer.ts +++ b/packages/grafana-data/src/geo/layer.ts @@ -2,74 +2,23 @@ import { Map as OpenLayersMap } from 'ol'; import BaseLayer from 'ol/layer/Base'; import { ReactNode } from 'react'; +import { MapLayerOptions, FrameGeometrySourceMode } from '@grafana/schema'; + import { EventBus } from '../events'; import { GrafanaTheme2 } from '../themes'; -import { MatcherConfig, PanelData } from '../types'; +import { PanelData } from '../types'; import { PanelOptionsEditorBuilder } from '../utils'; import { RegistryItemWithOptions } from '../utils/Registry'; /** - * @alpha + * @deprecated use the type from schema */ -export enum FrameGeometrySourceMode { - Auto = 'auto', // Will scan fields and find best match - Geohash = 'geohash', - Coords = 'coords', // lon field, lat field - Lookup = 'lookup', // keys > location - // H3 = 'h3', - // WKT = 'wkt, - // geojson? geometry text -} +export { FrameGeometrySourceMode }; /** - * @alpha + * @deprecated use the type from schema */ -export interface FrameGeometrySource { - mode: FrameGeometrySourceMode; - - // Field mappings - geohash?: string; - latitude?: string; - longitude?: string; - h3?: string; - wkt?: string; - lookup?: string; - - // Path to Gazetteer - gazetteer?: string; -} - -/** - * This gets saved in panel json - * - * depending on the type, it may have additional config - * - * This exists in `grafana/data` so the types are well known and extendable but the - * layout/frame is control by the map panel - * - * @alpha - */ -export interface MapLayerOptions { - type: string; - name: string; // configured unique display name - - // Custom options depending on the type - config?: TConfig; - - // Common method to define geometry fields - location?: FrameGeometrySource; - - // Defines which data query refId is associated with the layer - filterData?: MatcherConfig; - - // Common properties: - // https://openlayers.org/en/latest/apidoc/module-ol_layer_Base-BaseLayer.html - // Layer opacity (0-1) - opacity?: number; - - // Check tooltip (defaults to true) - tooltip?: boolean; -} +export type { FrameGeometrySource, MapLayerOptions } from '@grafana/schema'; /** * @alpha @@ -81,7 +30,7 @@ export interface MapLayerHandler { */ update?: (data: PanelData) => void; - /** Optional callback to cleaup before getting removed */ + /** Optional callback for cleanup before getting removed */ dispose?: () => void; /** return react node for the legend */ diff --git a/packages/grafana-data/src/types/transformations.ts b/packages/grafana-data/src/types/transformations.ts index 017a72bfc49..270ec1cbdda 100644 --- a/packages/grafana-data/src/types/transformations.ts +++ b/packages/grafana-data/src/types/transformations.ts @@ -1,3 +1,4 @@ +export type { MatcherConfig } from '@grafana/schema'; import { MonoTypeOperatorFunction } from 'rxjs'; import { RegistryItemWithOptions } from '../utils/Registry'; @@ -80,10 +81,6 @@ export interface ValueMatcherInfo extends RegistryItemWithOption isApplicable: (field: Field) => boolean; getDefaultOptions: (field: Field) => TOptions; } -export interface MatcherConfig { - id: string; - options?: TOptions; -} /** * @public diff --git a/packages/grafana-schema/src/common/common.gen.ts b/packages/grafana-schema/src/common/common.gen.ts index ab3f4bae139..e2f18410125 100644 --- a/packages/grafana-schema/src/common/common.gen.ts +++ b/packages/grafana-schema/src/common/common.gen.ts @@ -40,6 +40,43 @@ export interface DataQuery { refId: string; } +export interface MapLayerOptions { + /** + * Custom options depending on the type + */ + config?: unknown; + /** + * Defines a frame MatcherConfig that may filter data for the given layer + */ + filterData?: unknown; + /** + * Common method to define geometry fields + */ + location?: FrameGeometrySource; + /** + * configured unique display name + */ + name: string; + /** + * Common properties: + * https://openlayers.org/en/latest/apidoc/module-ol_layer_Base-BaseLayer.html + * Layer opacity (0-1) + */ + opacity?: number; + /** + * Check tooltip (defaults to true) + */ + tooltip?: boolean; + type: string; +} + +export enum FrameGeometrySourceMode { + Auto = 'auto', + Coords = 'coords', + Geohash = 'geohash', + Lookup = 'lookup', +} + /** * TODO docs */ @@ -595,6 +632,22 @@ export interface DataSourceRef { uid?: string; } +export interface FrameGeometrySource { + /** + * Path to Gazetteer + */ + gazetteer?: string; + /** + * Field mappings + */ + geohash?: string; + latitude?: string; + longitude?: string; + lookup?: string; + mode: FrameGeometrySourceMode; + wkt?: string; +} + /** * TODO docs */ diff --git a/packages/grafana-schema/src/common/geo.cue b/packages/grafana-schema/src/common/geo.cue new file mode 100644 index 00000000000..cd17654f8f9 --- /dev/null +++ b/packages/grafana-schema/src/common/geo.cue @@ -0,0 +1,33 @@ +package common + +MapLayerOptions: { + type: string + // configured unique display name + name: string + // Custom options depending on the type + config?: _ + // Common method to define geometry fields + location?: FrameGeometrySource + // Defines a frame MatcherConfig that may filter data for the given layer + filterData?: _ + // Common properties: + // https://openlayers.org/en/latest/apidoc/module-ol_layer_Base-BaseLayer.html + // Layer opacity (0-1) + opacity?: int64 + // Check tooltip (defaults to true) + tooltip?: bool +} @cuetsy(kind="interface") @grafana(TSVeneer="type") + +FrameGeometrySourceMode: "auto" | "geohash" |"coords" | "lookup" @cuetsy(kind="enum",memberNames="Auto|Geohash|Coords|Lookup") + +FrameGeometrySource: { + mode: FrameGeometrySourceMode + // Field mappings + geohash?: string + latitude?: string + longitude?: string + wkt?: string + lookup?: string + // Path to Gazetteer + gazetteer?: string +} @cuetsy(kind="interface") diff --git a/packages/grafana-schema/src/common/mudball.cue b/packages/grafana-schema/src/common/mudball.cue index ad648cccb68..d131253af8d 100644 --- a/packages/grafana-schema/src/common/mudball.cue +++ b/packages/grafana-schema/src/common/mudball.cue @@ -1,7 +1,5 @@ package common -// TODO break this up into individual files. Current limitation on this is codegen logic, imports, dependencies - // TODO docs AxisPlacement: "auto" | "top" | "right" | "bottom" | "left" | "hidden" @cuetsy(kind="enum") diff --git a/packages/grafana-schema/src/index.gen.ts b/packages/grafana-schema/src/index.gen.ts index 2fac4368b56..9fdf476a0e4 100644 --- a/packages/grafana-schema/src/index.gen.ts +++ b/packages/grafana-schema/src/index.gen.ts @@ -26,7 +26,6 @@ export type { SpecialValueMap, ValueMappingResult, Transformation, - MatcherConfig, RowPanel, GraphPanel, HeatmapPanel @@ -46,7 +45,6 @@ export { SpecialValueMatch, DashboardCursorSync, defaultDashboardCursorSync, - defaultMatcherConfig, defaultRowPanel } from './raw/dashboard/x/dashboard_types.gen'; @@ -65,6 +63,7 @@ export type { DataSourceRef, Panel, FieldConfigSource, + MatcherConfig, FieldConfig } from './veneer/dashboard.types'; @@ -83,6 +82,7 @@ export { VariableHide, defaultPanel, defaultFieldConfigSource, + defaultMatcherConfig, defaultFieldConfig } from './veneer/dashboard.types'; diff --git a/packages/grafana-schema/src/veneer/common.types.ts b/packages/grafana-schema/src/veneer/common.types.ts index cf65607d001..1e35686cbbe 100644 --- a/packages/grafana-schema/src/veneer/common.types.ts +++ b/packages/grafana-schema/src/veneer/common.types.ts @@ -1,5 +1,13 @@ import * as raw from '../common/common.gen'; +import { MatcherConfig } from './dashboard.types'; + +export interface MapLayerOptions extends raw.MapLayerOptions { + // Custom options depending on the type + config?: TConfig; + filterData?: MatcherConfig; +} + export interface DataQuery extends raw.DataQuery { // TODO remove explicit nulls datasource?: raw.DataSourceRef | null; diff --git a/packages/grafana-schema/src/veneer/dashboard.types.ts b/packages/grafana-schema/src/veneer/dashboard.types.ts index 53b1d22921c..bab4aa4450d 100644 --- a/packages/grafana-schema/src/veneer/dashboard.types.ts +++ b/packages/grafana-schema/src/veneer/dashboard.types.ts @@ -43,6 +43,10 @@ export interface FieldConfigSource> extends r defaults: FieldConfig; } +export interface MatcherConfig extends raw.MatcherConfig { + options?: TConfig; +} + export const defaultDashboard = raw.defaultDashboard as Dashboard; export const defaultVariableModel = { ...raw.defaultVariableModel, @@ -60,3 +64,4 @@ export const defaultVariableModel = { export const defaultPanel: Partial = raw.defaultPanel; export const defaultFieldConfig: Partial = raw.defaultFieldConfig; export const defaultFieldConfigSource: Partial = raw.defaultFieldConfigSource; +export const defaultMatcherConfig: Partial = raw.defaultMatcherConfig; diff --git a/public/app/features/geo/editor/locationEditor.ts b/public/app/features/geo/editor/locationEditor.ts index 5f7d6da044f..a1deb38f8a3 100644 --- a/public/app/features/geo/editor/locationEditor.ts +++ b/public/app/features/geo/editor/locationEditor.ts @@ -1,11 +1,5 @@ -import { - Field, - FieldType, - FrameGeometrySource, - FrameGeometrySourceMode, - PanelOptionsEditorBuilder, - DataFrame, -} from '@grafana/data'; +import { Field, FieldType, PanelOptionsEditorBuilder, DataFrame } from '@grafana/data'; +import { FrameGeometrySource, FrameGeometrySourceMode } from '@grafana/schema'; import { GazetteerPathEditor } from 'app/features/geo/editor/GazetteerPathEditor'; import { LocationModeEditor } from './locationModeEditor'; diff --git a/public/app/features/geo/editor/locationModeEditor.tsx b/public/app/features/geo/editor/locationModeEditor.tsx index 34cc551f4cd..204db9200c9 100644 --- a/public/app/features/geo/editor/locationModeEditor.tsx +++ b/public/app/features/geo/editor/locationModeEditor.tsx @@ -1,14 +1,9 @@ import { css } from '@emotion/css'; import React, { useEffect, useState } from 'react'; -import { - StandardEditorProps, - FrameGeometrySourceMode, - DataFrame, - FrameGeometrySource, - GrafanaTheme2, -} from '@grafana/data'; +import { StandardEditorProps, DataFrame, GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { FrameGeometrySource, FrameGeometrySourceMode } from '@grafana/schema'; import { Alert, HorizontalGroup, Icon, Select, useStyles2 } from '@grafana/ui'; import { FrameGeometryField, getGeometryField, getLocationMatchers } from '../utils/location'; diff --git a/public/app/features/geo/utils/location.test.ts b/public/app/features/geo/utils/location.test.ts index 18ce5d83b1a..548fa4fd666 100644 --- a/public/app/features/geo/utils/location.test.ts +++ b/public/app/features/geo/utils/location.test.ts @@ -1,7 +1,8 @@ import { Point } from 'ol/geom'; import { toLonLat } from 'ol/proj'; -import { toDataFrame, FieldType, FrameGeometrySourceMode } from '@grafana/data'; +import { toDataFrame, FieldType } from '@grafana/data'; +import { FrameGeometrySourceMode } from '@grafana/schema'; import { getGeometryField, getLocationFields, getLocationMatchers } from './location'; diff --git a/public/app/features/geo/utils/location.ts b/public/app/features/geo/utils/location.ts index 42661776f5a..5c0828a834f 100644 --- a/public/app/features/geo/utils/location.ts +++ b/public/app/features/geo/utils/location.ts @@ -1,8 +1,6 @@ import { Geometry } from 'ol/geom'; import { - FrameGeometrySource, - FrameGeometrySourceMode, FieldMatcher, getFieldMatcher, FieldMatcherID, @@ -11,6 +9,7 @@ import { getFieldDisplayName, FieldType, } from '@grafana/data'; +import { FrameGeometrySource, FrameGeometrySourceMode } from '@grafana/schema'; import { getGeoFieldFromGazetteer, pointFieldFromGeohash, pointFieldFromLonLat } from '../format/utils'; import { getGazetteer, Gazetteer } from '../gazetteer/gazetteer'; diff --git a/public/app/features/transformers/spatial/SpatialTransformerEditor.tsx b/public/app/features/transformers/spatial/SpatialTransformerEditor.tsx index e499d1845e6..7c29bb96faf 100644 --- a/public/app/features/transformers/spatial/SpatialTransformerEditor.tsx +++ b/public/app/features/transformers/spatial/SpatialTransformerEditor.tsx @@ -3,8 +3,6 @@ import React, { useEffect } from 'react'; import { DataTransformerID, - FrameGeometrySource, - FrameGeometrySourceMode, GrafanaTheme2, PanelOptionsEditorBuilder, PluginState, @@ -12,6 +10,7 @@ import { TransformerRegistryItem, TransformerUIProps, } from '@grafana/data'; +import { FrameGeometrySource, FrameGeometrySourceMode } from '@grafana/schema'; import { useTheme2 } from '@grafana/ui'; import { addLocationFields } from 'app/features/geo/editor/locationEditor'; diff --git a/public/app/features/transformers/spatial/models.gen.ts b/public/app/features/transformers/spatial/models.gen.ts index 7b30638ec4c..4def42da1c8 100644 --- a/public/app/features/transformers/spatial/models.gen.ts +++ b/public/app/features/transformers/spatial/models.gen.ts @@ -1,4 +1,4 @@ -import { FrameGeometrySource, FrameGeometrySourceMode } from '@grafana/data'; +import { FrameGeometrySource, FrameGeometrySourceMode } from '@grafana/schema'; // This file should be generated by cue schema diff --git a/public/app/plugins/panel/geomap/GeomapPanel.tsx b/public/app/plugins/panel/geomap/GeomapPanel.tsx index 06ac6b825a6..f4070048c01 100644 --- a/public/app/plugins/panel/geomap/GeomapPanel.tsx +++ b/public/app/plugins/panel/geomap/GeomapPanel.tsx @@ -25,7 +25,7 @@ import { GeomapHoverPayload } from './event'; import { getGlobalStyles } from './globalStyles'; import { defaultMarkersConfig } from './layers/data/markersLayer'; import { DEFAULT_BASEMAP_CONFIG } from './layers/registry'; -import { ControlsOptions, GeomapPanelOptions, MapLayerState, MapViewConfig, TooltipMode } from './types'; +import { ControlsOptions, PanelOptions, MapLayerState, MapViewConfig, TooltipMode } from './types'; import { getActions } from './utils/actions'; import { getLayersExtent } from './utils/getLayersExtent'; import { applyLayerFilter, initLayer } from './utils/layers'; @@ -36,7 +36,7 @@ import { centerPointRegistry, MapCenterID } from './view'; // Allows multiple panels to share the same view instance let sharedView: View | undefined = undefined; -type Props = PanelProps; +type Props = PanelProps; interface State extends OverlayProps { ttip?: GeomapHoverPayload; ttipOpen: boolean; @@ -144,7 +144,7 @@ export class GeomapPanel extends Component { * * NOTE: changes to basemap and layers are handled independently */ - optionsChanged(options: GeomapPanelOptions) { + optionsChanged(options: PanelOptions) { const oldOptions = this.props.options; if (options.view !== oldOptions.view) { const [updatedSharedView, view] = this.initMapView(options.view, sharedView); diff --git a/public/app/plugins/panel/geomap/editor/FitMapViewEditor.tsx b/public/app/plugins/panel/geomap/editor/FitMapViewEditor.tsx index 3c59c095ffb..738e5b238e9 100644 --- a/public/app/plugins/panel/geomap/editor/FitMapViewEditor.tsx +++ b/public/app/plugins/panel/geomap/editor/FitMapViewEditor.tsx @@ -4,13 +4,13 @@ import { SelectableValue, StandardEditorContext } from '@grafana/data'; import { InlineFieldRow, InlineField, RadioButtonGroup, Select } from '@grafana/ui'; import { NumberInput } from 'app/core/components/OptionsUI/NumberInput'; -import { GeomapInstanceState, GeomapPanelOptions, MapViewConfig } from '../types'; +import { GeomapInstanceState, PanelOptions, MapViewConfig } from '../types'; type Props = { labelWidth: number; value: MapViewConfig; onChange: (value?: MapViewConfig | undefined) => void; - context: StandardEditorContext; + context: StandardEditorContext; }; // Data scope options for 'Fit to data' diff --git a/public/app/plugins/panel/geomap/editor/LayersEditor.tsx b/public/app/plugins/panel/geomap/editor/LayersEditor.tsx index 52908a98c6a..db14dc55fe2 100644 --- a/public/app/plugins/panel/geomap/editor/LayersEditor.tsx +++ b/public/app/plugins/panel/geomap/editor/LayersEditor.tsx @@ -7,9 +7,9 @@ import { AddLayerButton } from 'app/core/components/Layers/AddLayerButton'; import { LayerDragDropList } from 'app/core/components/Layers/LayerDragDropList'; import { getLayersOptions } from '../layers/registry'; -import { GeomapPanelOptions, MapLayerState, GeomapInstanceState } from '../types'; +import { PanelOptions, MapLayerState, GeomapInstanceState } from '../types'; -type LayersEditorProps = StandardEditorProps; +type LayersEditorProps = StandardEditorProps; export const LayersEditor = (props: LayersEditorProps) => { const { layers, selected, actions } = props.context.instanceState ?? {}; diff --git a/public/app/plugins/panel/geomap/editor/MapViewEditor.tsx b/public/app/plugins/panel/geomap/editor/MapViewEditor.tsx index 905695d4cdd..749a80b2eec 100644 --- a/public/app/plugins/panel/geomap/editor/MapViewEditor.tsx +++ b/public/app/plugins/panel/geomap/editor/MapViewEditor.tsx @@ -5,7 +5,7 @@ import { StandardEditorProps, SelectableValue } from '@grafana/data'; import { Button, InlineField, InlineFieldRow, Select, VerticalGroup } from '@grafana/ui'; import { NumberInput } from 'app/core/components/OptionsUI/NumberInput'; -import { GeomapPanelOptions, MapViewConfig, GeomapInstanceState } from '../types'; +import { PanelOptions, MapViewConfig, GeomapInstanceState } from '../types'; import { centerPointRegistry, MapCenterID } from '../view'; import { CoordinatesMapViewEditor } from './CoordinatesMapViewEditor'; @@ -15,7 +15,7 @@ export const MapViewEditor = ({ value, onChange, context, -}: StandardEditorProps) => { +}: StandardEditorProps) => { const labelWidth = 10; const views = useMemo(() => { diff --git a/public/app/plugins/panel/geomap/layers/data/photosLayer.tsx b/public/app/plugins/panel/geomap/layers/data/photosLayer.tsx index 208a9bf4020..6a45231d6a3 100644 --- a/public/app/plugins/panel/geomap/layers/data/photosLayer.tsx +++ b/public/app/plugins/panel/geomap/layers/data/photosLayer.tsx @@ -1,14 +1,13 @@ import { MapLayerRegistryItem, - MapLayerOptions, PanelData, GrafanaTheme2, - FrameGeometrySourceMode, EventBus, PluginState, FieldType, Field, } from '@grafana/data'; +import { FrameGeometrySourceMode, MapLayerOptions } from '@grafana/schema'; import Map from 'ol/Map'; import { FeatureLike } from 'ol/Feature'; import { getLocationMatchers } from 'app/features/geo/utils/location'; diff --git a/public/app/plugins/panel/geomap/layers/data/routeLayer.tsx b/public/app/plugins/panel/geomap/layers/data/routeLayer.tsx index 6ae014f2043..0b603506fe7 100644 --- a/public/app/plugins/panel/geomap/layers/data/routeLayer.tsx +++ b/public/app/plugins/panel/geomap/layers/data/routeLayer.tsx @@ -1,9 +1,7 @@ import { MapLayerRegistryItem, - MapLayerOptions, PanelData, GrafanaTheme2, - FrameGeometrySourceMode, PluginState, EventBus, DataHoverEvent, @@ -11,6 +9,12 @@ import { DataFrame, TIME_SERIES_TIME_FIELD_NAME, } from '@grafana/data'; + +import { + MapLayerOptions, + FrameGeometrySourceMode, +} from '@grafana/schema'; + import Map from 'ol/Map'; import { FeatureLike } from 'ol/Feature'; import { Subscription, throttleTime } from 'rxjs'; diff --git a/public/app/plugins/panel/geomap/migrations.ts b/public/app/plugins/panel/geomap/migrations.ts index e7a4d991143..7342d0982e8 100644 --- a/public/app/plugins/panel/geomap/migrations.ts +++ b/public/app/plugins/panel/geomap/migrations.ts @@ -6,7 +6,7 @@ import { ResourceDimensionMode } from 'app/features/dimensions'; import { MarkersConfig } from './layers/data/markersLayer'; import { getMarkerAsPath } from './style/markers'; import { defaultStyleConfig } from './style/types'; -import { GeomapPanelOptions, TooltipMode } from './types'; +import { PanelOptions, TooltipMode } from './types'; import { MapCenterID } from './view'; /** @@ -26,13 +26,13 @@ export const mapPanelChangedHandler: PanelTypeChangedHandler = (panel, prevPlugi return {}; }; -export function worldmapToGeomapOptions(angular: any): { fieldConfig: FieldConfigSource; options: GeomapPanelOptions } { +export function worldmapToGeomapOptions(angular: any): { fieldConfig: FieldConfigSource; options: PanelOptions } { const fieldConfig: FieldConfigSource = { defaults: {}, overrides: [], }; - const options: GeomapPanelOptions = { + const options: PanelOptions = { view: { id: MapCenterID.Zero, }, @@ -107,7 +107,7 @@ function asNumber(v: any): number | undefined { return isNaN(num) ? undefined : num; } -export const mapMigrationHandler = (panel: PanelModel): Partial => { +export const mapMigrationHandler = (panel: PanelModel): Partial => { const pluginVersion = panel?.pluginVersion ?? ''; // before 8.3, only one layer was supported! diff --git a/public/app/plugins/panel/geomap/models.cue b/public/app/plugins/panel/geomap/models.cue new file mode 100644 index 00000000000..d502a796ee4 --- /dev/null +++ b/public/app/plugins/panel/geomap/models.cue @@ -0,0 +1,76 @@ +// Copyright 2023 Grafana Labs +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package grafanaplugin + +import ( + "github.com/grafana/thema" + ui "github.com/grafana/grafana/packages/grafana-schema/src/common" +) + +Panel: thema.#Lineage & { + name: "geomap" + seqs: [ + { + schemas: [ + { + PanelOptions: { + view: MapViewConfig + controls: ControlsOptions + basemap: ui.MapLayerOptions + layers: [...ui.MapLayerOptions] + tooltip: TooltipOptions + } @cuetsy(kind="interface") + + MapViewConfig: { + id: string | *"zero" + lat?: int64 | *0 + lon?: int64 | *0 + zoom?: int64 | *1 + minZoom?: int64 + maxZoom?: int64 + padding?: int64 + allLayers?: bool | *true + lastOnly?: bool + layer?: string + shared?: bool + } @cuetsy(kind="interface") + + ControlsOptions: { + // Zoom (upper left) + showZoom?: bool + // let the mouse wheel zoom + mouseWheelZoom?: bool + // Lower right + showAttribution?: bool + // Scale options + showScale?: bool + // Show debug + showDebug?: bool + // Show measure + showMeasure?: bool + } @cuetsy(kind="interface") + + TooltipOptions: { + mode: TooltipMode + } @cuetsy(kind="interface") + + TooltipMode: "none" | "details" @cuetsy(kind="enum",memberNames="None|Details") + + MapCenterID: "zero"|"coords"|"fit" @cuetsy(kind="enum",members="Zero|Coordinates|Fit") + }, + ] + }, + ] +} diff --git a/public/app/plugins/panel/geomap/models.gen.ts b/public/app/plugins/panel/geomap/models.gen.ts new file mode 100644 index 00000000000..14e35dcc58e --- /dev/null +++ b/public/app/plugins/panel/geomap/models.gen.ts @@ -0,0 +1,89 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. +// +// Generated by: +// public/app/plugins/gen.go +// Using jennies: +// TSTypesJenny +// PluginTSTypesJenny +// +// Run 'make gen-cue' from repository root to regenerate. + +import * as ui from '@grafana/schema'; + +export const PanelModelVersion = Object.freeze([0, 0]); + +export interface PanelOptions { + basemap: ui.MapLayerOptions; + controls: ControlsOptions; + layers: Array; + tooltip: TooltipOptions; + view: MapViewConfig; +} + +export const defaultPanelOptions: Partial = { + layers: [], +}; + +export interface MapViewConfig { + allLayers?: boolean; + id: string; + lastOnly?: boolean; + lat?: number; + layer?: string; + lon?: number; + maxZoom?: number; + minZoom?: number; + padding?: number; + shared?: boolean; + zoom?: number; +} + +export const defaultMapViewConfig: Partial = { + allLayers: true, + id: 'zero', + lat: 0, + lon: 0, + zoom: 1, +}; + +export interface ControlsOptions { + /** + * let the mouse wheel zoom + */ + mouseWheelZoom?: boolean; + /** + * Lower right + */ + showAttribution?: boolean; + /** + * Show debug + */ + showDebug?: boolean; + /** + * Show measure + */ + showMeasure?: boolean; + /** + * Scale options + */ + showScale?: boolean; + /** + * Zoom (upper left) + */ + showZoom?: boolean; +} + +export interface TooltipOptions { + mode: TooltipMode; +} + +export enum TooltipMode { + Details = 'details', + None = 'none', +} + +export enum MapCenterID { + Coords = 'coords', + Fit = 'fit', + Zero = 'zero', +} diff --git a/public/app/plugins/panel/geomap/module.tsx b/public/app/plugins/panel/geomap/module.tsx index 9a9a9e3738d..c03f0289d2f 100644 --- a/public/app/plugins/panel/geomap/module.tsx +++ b/public/app/plugins/panel/geomap/module.tsx @@ -9,9 +9,9 @@ import { LayersEditor } from './editor/LayersEditor'; import { MapViewEditor } from './editor/MapViewEditor'; import { getLayerEditor } from './editor/layerEditor'; import { mapPanelChangedHandler, mapMigrationHandler } from './migrations'; -import { defaultView, GeomapPanelOptions, TooltipMode, GeomapInstanceState } from './types'; +import { defaultMapViewConfig, PanelOptions, TooltipMode, GeomapInstanceState } from './types'; -export const plugin = new PanelPlugin(GeomapPanel) +export const plugin = new PanelPlugin(GeomapPanel) .setNoPadding() .setPanelChangeHandler(mapPanelChangedHandler) .setMigrationHandler(mapMigrationHandler) @@ -29,7 +29,7 @@ export const plugin = new PanelPlugin(GeomapPanel) name: 'Initial view', // don't show it description: 'This location will show when the panel first loads.', editor: MapViewEditor, - defaultValue: defaultView, + defaultValue: defaultMapViewConfig, }); builder.addBooleanSwitch({ @@ -37,7 +37,7 @@ export const plugin = new PanelPlugin(GeomapPanel) path: 'view.shared', description: 'Use the same view across multiple panels. Note: this may require a dashboard reload.', name: 'Share view', - defaultValue: defaultView.shared, + defaultValue: defaultMapViewConfig.shared, }); // eslint-disable-next-line diff --git a/public/app/plugins/panel/geomap/types.ts b/public/app/plugins/panel/geomap/types.ts index 7fbe156bb9d..2a913c8dde5 100644 --- a/public/app/plugins/panel/geomap/types.ts +++ b/public/app/plugins/panel/geomap/types.ts @@ -5,75 +5,13 @@ import BaseLayer from 'ol/layer/Base'; import { Subject } from 'rxjs'; import { MapLayerHandler, MapLayerOptions } from '@grafana/data'; -import { HideableFieldConfig } from '@grafana/schema'; import { LayerElement } from 'app/core/components/Layers/types'; +import { ControlsOptions as ControlsOptionsBase } from './models.gen'; import { StyleConfig } from './style/types'; -import { MapCenterID } from './view'; -export interface ControlsOptions { - // Zoom (upper left) - showZoom?: boolean; - - // let the mouse wheel zoom - mouseWheelZoom?: boolean; - - // Lower right - showAttribution?: boolean; - - // Scale options - showScale?: boolean; +export interface ControlsOptions extends ControlsOptionsBase { scaleUnits?: Units; - - // Show debug - showDebug?: boolean; - - // Show measure - showMeasure?: boolean; -} - -export enum TooltipMode { - None = 'none', - Details = 'details', -} - -export interface TooltipOptions { - mode: TooltipMode; -} - -export interface MapViewConfig { - id: string; // placename > lookup - lat?: number; - lon?: number; - zoom?: number; - minZoom?: number; - maxZoom?: number; - padding?: number; - allLayers?: boolean; - lastOnly?: boolean; - layer?: string; - shared?: boolean; -} - -export const defaultView: MapViewConfig = { - id: MapCenterID.Zero, - lat: 0, - lon: 0, - zoom: 1, - allLayers: true, -}; - -/** Support hide from legend/tooltip */ -export interface GeomapFieldConfig extends HideableFieldConfig { - // nothing custom yet -} - -export interface GeomapPanelOptions { - view: MapViewConfig; - controls: ControlsOptions; - basemap: MapLayerOptions; - layers: MapLayerOptions[]; - tooltip: TooltipOptions; } export interface FeatureStyleConfig { @@ -122,3 +60,5 @@ export interface MapLayerState extends LayerElement { isBasemap?: boolean; mouseEvents: Subject; } + +export { PanelOptions, MapViewConfig, TooltipOptions, TooltipMode, defaultMapViewConfig } from './models.gen'; diff --git a/public/app/plugins/panel/geomap/utils/actions.ts b/public/app/plugins/panel/geomap/utils/actions.ts index 52e4530de49..2461190e154 100644 --- a/public/app/plugins/panel/geomap/utils/actions.ts +++ b/public/app/plugins/panel/geomap/utils/actions.ts @@ -1,6 +1,6 @@ import { cloneDeep } from 'lodash'; -import { FrameGeometrySourceMode } from '@grafana/data/src'; +import { FrameGeometrySourceMode } from '@grafana/schema'; import { GeomapPanel } from '../GeomapPanel'; import { geomapLayerRegistry } from '../layers/registry'; diff --git a/public/app/plugins/panel/geomap/utils/utils.ts b/public/app/plugins/panel/geomap/utils/utils.ts index d920369b7ad..52c978cc30d 100644 --- a/public/app/plugins/panel/geomap/utils/utils.ts +++ b/public/app/plugins/panel/geomap/utils/utils.ts @@ -8,7 +8,7 @@ import { getGrafanaDatasource } from 'app/plugins/datasource/grafana/datasource' import { GeomapPanel } from '../GeomapPanel'; import { defaultStyleConfig, StyleConfig, StyleConfigState, StyleDimensions } from '../style/types'; -import { GeomapPanelOptions, MapLayerState } from '../types'; +import { PanelOptions, MapLayerState } from '../types'; export function getStyleDimension( frame: DataFrame | undefined, @@ -74,7 +74,7 @@ async function initGeojsonFiles() { } } -export const getNewOpenLayersMap = (panel: GeomapPanel, options: GeomapPanelOptions, div: HTMLDivElement) => { +export const getNewOpenLayersMap = (panel: GeomapPanel, options: PanelOptions, div: HTMLDivElement) => { const [view] = panel.initMapView(options.view, undefined); return (panel.map = new OpenLayersMap({ view: view, @@ -88,7 +88,7 @@ export const getNewOpenLayersMap = (panel: GeomapPanel, options: GeomapPanelOpti })); }; -export const updateMap = (panel: GeomapPanel, options: GeomapPanelOptions) => { +export const updateMap = (panel: GeomapPanel, options: PanelOptions) => { panel.initControls(options.controls); panel.forceUpdate(); // first render }; From 1181d06b1be98011ca335125b89269c504393542 Mon Sep 17 00:00:00 2001 From: Brendan O'Handley Date: Mon, 23 Jan 2023 18:13:22 -0500 Subject: [PATCH 15/46] Graphite: Have metric name type query variable use metric render endpoint (#61730) * have metric name query var use metric render endpoint * add documentation for new query types --- .../graphite/template-variables/index.md | 12 ++++- .../datasource/graphite/datasource.test.ts | 25 ++++++++--- .../plugins/datasource/graphite/datasource.ts | 44 ++++++++++++++----- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/docs/sources/datasources/graphite/template-variables/index.md b/docs/sources/datasources/graphite/template-variables/index.md index 36a8d51f66c..e62cb2d5ca3 100644 --- a/docs/sources/datasources/graphite/template-variables/index.md +++ b/docs/sources/datasources/graphite/template-variables/index.md @@ -21,6 +21,16 @@ Grafana refers to such variables as template variables. For an introduction to templating and template variables, refer to the [Templating]({{< relref "../../../dashboards/variables" >}}) and [Add and manage variables]({{< relref "../../../dashboards/variables/add-template-variables" >}}) documentation. +## Select a query type + +There are three query types for Graphite template variables + +| Query Type | Description | +| ----------------- | ------------------------------------------------------------------------------- | +| Default Query | Use functions such as `tags()`, `tag_values()`, `expand()` and metrics. | +| Value Query | Returns all the values for a query that includes a metric and function. | +| Metric Name Query | Returns all the names for a query that includes a metric and function. | + ## Use tag variables To create a variable using tag values, use the Grafana functions `tags` and `tag_values`. @@ -40,7 +50,7 @@ tag_values(server, server=~backend\*, app=~${apps:regex}) For details, refer to the [Graphite docs on the autocomplete API for tags](http://graphite.readthedocs.io/en/latest/tags.html#auto-complete-support). -### Use multi-valie variables in tag queries +### Use multi-value variables in tag queries Multi-value variables in tag queries use the advanced formatting syntax for variables introduced in Grafana v5.0: `{var:regex}`. Non-tag queries use the default glob formatting for multi-value variables. diff --git a/public/app/plugins/datasource/graphite/datasource.test.ts b/public/app/plugins/datasource/graphite/datasource.test.ts index 9910231df87..45eda7f16b2 100644 --- a/public/app/plugins/datasource/graphite/datasource.test.ts +++ b/public/app/plugins/datasource/graphite/datasource.test.ts @@ -624,24 +624,35 @@ describe('graphiteDatasource', () => { fetchMock.mockImplementation((options: any) => { requestOptions = options; return of( - createFetchResponse({ - results: ['apps.backend.backend_01', 'apps.backend.backend_02', 'apps.country.IE', 'apps.country.SE'], - }) + createFetchResponse([ + { + target: 'apps.backend.backend_01', + datapoints: [ + [10, 1], + [12, 1], + ], + }, + { + target: 'apps.backend.backend_02', + datapoints: [ + [10, 1], + [12, 1], + ], + }, + ]) ); }); const fq: GraphiteQuery = { queryType: GraphiteQueryType.MetricName, - target: 'query', + target: 'apps.backend.*', refId: 'A', datasource: ctx.ds, }; const data = await ctx.ds.metricFindQuery(fq); - expect(requestOptions.url).toBe('/api/datasources/proxy/1/metrics/expand'); + expect(requestOptions.url).toBe('/api/datasources/proxy/1/render'); expect(data[0].text).toBe('apps.backend.backend_01'); expect(data[1].text).toBe('apps.backend.backend_02'); - expect(data[2].text).toBe('apps.country.IE'); - expect(data[3].text).toBe('apps.country.SE'); }); }); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index ba3643684f8..ee3a4f7f6c0 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -486,8 +486,8 @@ export class GraphiteDatasource const options: any = optionalOptions || {}; const queryObject = convertToGraphiteQueryObject(findQuery); - if (queryObject.queryType === GraphiteQueryType.Value) { - return this.requestMetricRender(queryObject, options); + if (queryObject.queryType === GraphiteQueryType.Value || queryObject.queryType === GraphiteQueryType.MetricName) { + return this.requestMetricRender(queryObject, options, queryObject.queryType); } let query = queryObject.target ?? ''; @@ -531,7 +531,7 @@ export class GraphiteDatasource }; } - if (useExpand || queryObject.queryType === GraphiteQueryType.MetricName) { + if (useExpand) { return this.requestMetricExpand(interpolatedQuery, options.requestId, range); } else { return this.requestMetricFind(interpolatedQuery, options.requestId, range); @@ -540,14 +540,22 @@ export class GraphiteDatasource /** * Search for metrics matching giving pattern using /metrics/render endpoint. - * It will return all possible values and parse them based on queryType. + * It will return all possible values or names and parse them based on queryType. * For example: * * queryType: GraphiteQueryType.Value * query: groupByNode(movingAverage(apps.country.IE.counters.requests.count, 10), 2, 'sum') * result: 239.4, 233.4, 230.8, 230.4, 233.9, 238, 239.8, 236.8, 235.8 + * + * queryType: GraphiteQueryType.MetricName + * query: highestAverage(carbon.agents.*.*, 5) + * result: carbon.agents.aa6338c54341-a.memUsage, carbon.agents.aa6338c54341-a.committedPoints, carbon.agents.aa6338c54341-a.updateOperations, carbon.agents.aa6338c54341-a.metricsReceived, carbon.agents.aa6338c54341-a.activeConnections */ - private async requestMetricRender(queryObject: GraphiteQuery, options: any): Promise { + private async requestMetricRender( + queryObject: GraphiteQuery, + options: any, + queryType: GraphiteQueryType + ): Promise { const requestId: string = options.requestId ?? `Q${this.requestCounter++}`; const range: TimeRange = options.range ?? { from: dateTime().subtract(6, 'hour'), @@ -568,14 +576,28 @@ export class GraphiteDatasource requestId, range, }; - const data = await lastValueFrom(this.query(queryReq)); - const result: MetricFindValue[] = data.data[0].fields[1].values - .filter((f?: number) => !!f) - .map((v: number) => ({ - text: v.toString(), - value: v, + const data: DataQueryResponse = await lastValueFrom(this.query(queryReq)); + + let result: MetricFindValue[]; + + if (queryType === GraphiteQueryType.Value) { + result = data.data[0].fields[1].values + .filter((f?: number) => !!f) + .map((v: number) => ({ + text: v.toString(), + value: v, + expandable: false, + })); + } else if (queryType === GraphiteQueryType.MetricName) { + result = data.data.map((series) => ({ + text: series.name, + value: series.name, expandable: false, })); + } else { + result = []; + } + return Promise.resolve(result); } From f5743ea9ac6fbf99e3f306fa81c255a4a1569866 Mon Sep 17 00:00:00 2001 From: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Date: Mon, 23 Jan 2023 18:38:04 -0600 Subject: [PATCH 16/46] Canvas: Improve arrow positioning when border is present (#61961) --- public/app/plugins/panel/canvas/ConnectionSVG.tsx | 9 ++++----- public/app/plugins/panel/canvas/Connections.tsx | 14 ++++++-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/public/app/plugins/panel/canvas/ConnectionSVG.tsx b/public/app/plugins/panel/canvas/ConnectionSVG.tsx index 6c54415f3f7..b0e99467898 100644 --- a/public/app/plugins/panel/canvas/ConnectionSVG.tsx +++ b/public/app/plugins/panel/canvas/ConnectionSVG.tsx @@ -119,9 +119,8 @@ export const ConnectionSVG = ({ setSVGRef, setLineRef, scene }: Props) => { return; } - const parentBorderWidth = parseFloat(getComputedStyle(parent).borderWidth); - const sourceHorizontalCenter = sourceRect.left - parentRect.left - parentBorderWidth + sourceRect.width / 2; - const sourceVerticalCenter = sourceRect.top - parentRect.top - parentBorderWidth + sourceRect.height / 2; + const sourceHorizontalCenter = sourceRect.left - parentRect.left + sourceRect.width / 2; + const sourceVerticalCenter = sourceRect.top - parentRect.top + sourceRect.height / 2; // Convert from connection coords to DOM coords // TODO: Break this out into util function and add tests @@ -134,8 +133,8 @@ export const ConnectionSVG = ({ setSVGRef, setLineRef, scene }: Props) => { if (info.targetName) { const targetRect = target.div?.getBoundingClientRect(); - const targetHorizontalCenter = targetRect!.left - parentRect.left - parentBorderWidth + targetRect!.width / 2; - const targetVerticalCenter = targetRect!.top - parentRect.top - parentBorderWidth + targetRect!.height / 2; + const targetHorizontalCenter = targetRect!.left - parentRect.left + targetRect!.width / 2; + const targetVerticalCenter = targetRect!.top - parentRect.top + targetRect!.height / 2; x2 = targetHorizontalCenter + (info.target.x * targetRect!.width) / 2; y2 = targetVerticalCenter - (info.target.y * targetRect!.height) / 2; diff --git a/public/app/plugins/panel/canvas/Connections.tsx b/public/app/plugins/panel/canvas/Connections.tsx index b3c46fbbee2..d1ae8dd954b 100644 --- a/public/app/plugins/panel/canvas/Connections.tsx +++ b/public/app/plugins/panel/canvas/Connections.tsx @@ -55,10 +55,9 @@ export class Connections { const elementBoundingRect = element!.getBoundingClientRect(); const parentBoundingRect = this.scene.div?.getBoundingClientRect(); - let parentBorderWidth = parseFloat(getComputedStyle(this.scene.div!).borderWidth); - const relativeTop = elementBoundingRect.top - (parentBoundingRect?.top ?? 0) - parentBorderWidth; - const relativeLeft = elementBoundingRect.left - (parentBoundingRect?.left ?? 0) - parentBorderWidth; + const relativeTop = elementBoundingRect.top - (parentBoundingRect?.top ?? 0); + const relativeLeft = elementBoundingRect.left - (parentBoundingRect?.left ?? 0); if (this.connectionAnchorDiv) { this.connectionAnchorDiv.style.display = 'none'; @@ -95,10 +94,9 @@ export class Connections { const sourceRect = this.connectionSource.div.getBoundingClientRect(); const parentRect = this.connectionSource.div.parentElement.getBoundingClientRect(); - const parentBorderWidth = parseFloat(getComputedStyle(this.connectionSource.div.parentElement).borderWidth); - const sourceVerticalCenter = sourceRect.top - parentRect.top - parentBorderWidth + sourceRect.height / 2; - const sourceHorizontalCenter = sourceRect.left - parentRect.left - parentBorderWidth + sourceRect.width / 2; + const sourceVerticalCenter = sourceRect.top - parentRect.top + sourceRect.height / 2; + const sourceHorizontalCenter = sourceRect.left - parentRect.left + sourceRect.width / 2; // Convert from DOM coords to connection coords // TODO: Break this out into util function and add tests @@ -112,8 +110,8 @@ export class Connections { if (this.connectionTarget && this.connectionTarget.div) { const targetRect = this.connectionTarget.div.getBoundingClientRect(); - const targetVerticalCenter = targetRect.top - parentRect.top - parentBorderWidth + targetRect.height / 2; - const targetHorizontalCenter = targetRect.left - parentRect.left - parentBorderWidth + targetRect.width / 2; + const targetVerticalCenter = targetRect.top - parentRect.top + targetRect.height / 2; + const targetHorizontalCenter = targetRect.left - parentRect.left + targetRect.width / 2; targetX = (x - targetHorizontalCenter) / (targetRect.width / 2); targetY = (targetVerticalCenter - y) / (targetRect.height / 2); From 6f26333e96aa1d5660be3291a89b5e7e39d76781 Mon Sep 17 00:00:00 2001 From: Selene Date: Tue, 24 Jan 2023 02:36:46 +0100 Subject: [PATCH 17/46] Chore: Add cuefix hook (#61941) * Add cue * Add fix-cue target * Add pre-commit hook * fixup! Add fix-cue target * Update the cue version * Add datasources and panels folders * Update cue files Co-authored-by: Tania B --- .bingo/Variables.mk | 6 ++++++ .bingo/cue.mod | 5 +++++ .bingo/cue.sum | 30 ++++++++++++++++++++++++++++++ .bingo/variables.env | 2 ++ Makefile | 8 +++++++- kinds/dashboard/dashboard_kind.cue | 4 +++- kinds/playlist/playlist_kind.cue | 11 ++++++----- kinds/team/team_kind.cue | 6 ++++-- package.json | 6 ++++++ 9 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 .bingo/cue.mod create mode 100644 .bingo/cue.sum diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 1adc68d6ec1..257da6358d5 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -23,6 +23,12 @@ $(BRA): $(BINGO_DIR)/bra.mod @echo "(re)installing $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bra.mod -o=$(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 "github.com/unknwon/bra" +CUE := $(GOBIN)/cue-v0.5.0-beta.2 +$(CUE): $(BINGO_DIR)/cue.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/cue-v0.5.0-beta.2" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cue.mod -o=$(GOBIN)/cue-v0.5.0-beta.2 "cuelang.org/go/cmd/cue" + DRONE := $(GOBIN)/drone-v1.5.0 $(DRONE): $(BINGO_DIR)/drone.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. diff --git a/.bingo/cue.mod b/.bingo/cue.mod new file mode 100644 index 00000000000..79ccdfb391c --- /dev/null +++ b/.bingo/cue.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.19 + +require cuelang.org/go v0.5.0-beta.2 // cmd/cue diff --git a/.bingo/cue.sum b/.bingo/cue.sum new file mode 100644 index 00000000000..f59e3b21cb6 --- /dev/null +++ b/.bingo/cue.sum @@ -0,0 +1,30 @@ +cuelang.org/go v0.4.3 h1:W3oBBjDTm7+IZfCKZAmC8uDG0eYfJL4Pp/xbbCMKaVo= +cuelang.org/go v0.4.3/go.mod h1:7805vR9H+VoBNdWFdI7jyDR3QLUPp4+naHfbcgp55HI= +cuelang.org/go v0.5.0-beta.2 h1:am5M7jGvNTJ0rnjrFNyvE7fucL/wRqb0emK4XxdThQI= +cuelang.org/go v0.5.0-beta.2/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw= +github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc h1:gSVONBi2HWMFXCa9jFdYvYk7IwW/mTLxWOF7rXS4LO0= +github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ= +golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9 h1:VtCrPQXM5Wo9l7XN64SjBMczl48j8mkP+2e3OhYlz+0= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/tools v0.0.0-20200612220849-54c614fe050c h1:g6oFfz6Cmw68izP3xsdud3Oxu145IPkeFzyRg58AKHM= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/.bingo/variables.env b/.bingo/variables.env index e58d95eca8b..7cb74529282 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -10,6 +10,8 @@ fi BRA="${GOBIN}/bra-v0.0.0-20200517080246-1e3013ecaff8" +CUE="${GOBIN}/cue-v0.5.0-beta.2" + DRONE="${GOBIN}/drone-v1.5.0" GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.50.1" diff --git a/Makefile b/Makefile index 07dba75646a..78cb6f15963 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,13 @@ WIRE_TAGS = "oss" -include local/Makefile include .bingo/Variables.mk -.PHONY: all deps-go deps-js deps build-go build-backend build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue +.PHONY: all deps-go deps-js deps build-go build-backend build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue fix-cue GO = go GO_FILES ?= ./pkg/... SH_FILES ?= $(shell find ./scripts -name *.sh) GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev) +GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev) GO_BUILD_FLAGS += $(if $(GO_BUILD_TAGS),-build-tags=$(GO_BUILD_TAGS)) all: deps build @@ -75,6 +76,11 @@ gen-go: $(WIRE) gen-cue @echo "generate go files" $(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server ./pkg/cmd/grafana-cli/runner +fix-cue: $(CUE) + @echo "formatting cue files" + $(CUE) fix kinds/**/*.cue + $(CUE) fix public/app/plugins/**/**/*.cue + gen-jsonnet: go generate ./devenv/jsonnet diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index d792acc924a..74a478b786d 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -8,7 +8,8 @@ maturity: "experimental" lineage: seqs: [ { schemas: [ - {// 0.0 + // 0.0 + { @grafana(TSVeneer="type") // Unique numeric identifier for the dashboard. @@ -506,6 +507,7 @@ lineage: seqs: [ } ... } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) + #HeatmapPanel: { type: "heatmap" @grafanamaturity(NeedsExpertReview) ... diff --git a/kinds/playlist/playlist_kind.cue b/kinds/playlist/playlist_kind.cue index 6b3865b9d85..2dbb7ed2942 100644 --- a/kinds/playlist/playlist_kind.cue +++ b/kinds/playlist/playlist_kind.cue @@ -1,12 +1,13 @@ package kind -name: "Playlist" +name: "Playlist" maturity: "merged" lineage: seqs: [ { schemas: [ - {//0.0 + //0.0 + { // Unique playlist identifier. Generated on creation, either by the // creator of the playlist of by the application. uid: string @@ -39,9 +40,9 @@ lineage: seqs: [ value: string // Title is an unused property -- it will be removed in the future - title?: string + title?: string } @cuetsy(kind="interface") - } + }, ] - } + }, ] diff --git a/kinds/team/team_kind.cue b/kinds/team/team_kind.cue index a7214bd2082..73cbc9b761d 100644 --- a/kinds/team/team_kind.cue +++ b/kinds/team/team_kind.cue @@ -1,6 +1,6 @@ package kind -name: "Team" +name: "Team" maturity: "merged" lineage: seqs: [ @@ -21,7 +21,9 @@ lineage: seqs: [ // TODO - it seems it's a team_member.permission, unlikely it should belong to the team kind permission: #Permission @grafanamaturity(ToMetadata="kind", MaybeRemove) // AccessControl metadata associated with a given resource. - accessControl?: [string]: bool @grafanamaturity(ToMetadata="sys") + accessControl?: { + [string]: bool @grafanamaturity(ToMetadata="sys") + } // Created indicates when the team was created. created: int64 @grafanamaturity(ToMetadata="sys") // Updated indicates when the team was updated. diff --git a/package.json b/package.json index 9e52e930895..52b4ff06286 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,12 @@ ], "*pkg/**/*.go": [ "gofmt -w -s" + ], + "*kinds/**/*.cue": [ + "make fix-cue" + ], + "*public/app/plugins/**/**/*.cue": [ + "make fix-cue" ] }, "devDependencies": { From c621693db056ad00938dcb755659607abed2a908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20L=C3=B3pez=20de=20la=20Franca=20Beltran?= <5459617+joanlopez@users.noreply.github.com> Date: Tue, 24 Jan 2023 02:47:42 +0100 Subject: [PATCH 18/46] Kindsys: Include generated-code links to Kinds report (#61910) * Kindsys: Include generated-code links to Kinds report * Apply feedback suggestions --- pkg/kindsys/report.go | 177 ++++- pkg/kindsys/report.json | 1538 ++++++++++++++++++++++++++------------- 2 files changed, 1186 insertions(+), 529 deletions(-) diff --git a/pkg/kindsys/report.go b/pkg/kindsys/report.go index a554f685674..cd565cfa0d2 100644 --- a/pkg/kindsys/report.go +++ b/pkg/kindsys/report.go @@ -10,6 +10,8 @@ import ( "encoding/json" "fmt" "os" + "path" + "reflect" "sort" "strings" @@ -18,9 +20,26 @@ import ( "github.com/grafana/grafana/pkg/plugins/pfs/corelist" "github.com/grafana/grafana/pkg/plugins/plugindef" "github.com/grafana/grafana/pkg/registry/corekind" + "github.com/grafana/thema" ) -const reportFileName = "report.json" +const ( + // Program's output + reportFileName = "report.json" + + // External references + repoBaseURL = "https://github.com/grafana/grafana/tree/main" + docsBaseURL = "https://grafana.com/docs/grafana/next/developers/kinds" + + // Local references + coreTSPath = "packages/grafana-schema/src/raw/%s/%s/%s_types.gen.ts" + coreGoPath = "pkg/kinds/%s" + coreCUEPath = "kinds/%s/%s_kind.cue" + + composableTSPath = "public/app/plugins/%s/%s/%s.gen.ts" + composableGoPath = "pkg/tsdb/%s/kinds/%s/types_%s_gen.go" + composableCUEPath = "public/app/plugins/%s/%s/%s.cue" +) func main() { report := buildKindStateReport() @@ -54,17 +73,58 @@ var plannedCoreKinds = []string{ "QueryHistory", } -type KindStateReport struct { - Kinds map[string]kindsys.SomeKindProperties `json:"kinds"` - Dimensions map[string]Dimension `json:"dimensions"` +type KindLinks struct { + Schema string + Go string + Ts string + Docs string } -func (r *KindStateReport) add(k kindsys.SomeKindProperties, category string) { +type Kind struct { + kindsys.SomeKindProperties + Category string + Links KindLinks +} + +// MarshalJSON is overwritten to marshal +// kindsys.SomeKindProperties at root level. +func (k Kind) MarshalJSON() ([]byte, error) { + b, err := json.Marshal(k.SomeKindProperties) + if err != nil { + return nil, err + } + + var m map[string]interface{} + if err = json.Unmarshal(b, &m); err != nil { + return nil, err + } + + m["category"] = k.Category + + m["links"] = map[string]string{} + for _, ref := range []string{"Schema", "Go", "Ts", "Docs"} { + refVal := reflect.ValueOf(k.Links).FieldByName(ref).String() + if len(refVal) > 0 { + m["links"].(map[string]string)[toCamelCase(ref)] = refVal + } else { + m["links"].(map[string]string)[toCamelCase(ref)] = "n/a" + } + } + + return json.Marshal(m) +} + +type KindStateReport struct { + Kinds map[string]Kind `json:"kinds"` + Dimensions map[string]Dimension `json:"dimensions"` +} + +func (r *KindStateReport) add(k Kind) { kName := k.Common().MachineName r.Kinds[kName] = k r.Dimensions["maturity"][k.Common().Maturity.String()].add(kName) - r.Dimensions["category"][category].add(kName) + r.Dimensions["category"][k.Category].add(kName) } type Dimension map[string]*DimensionValue @@ -85,7 +145,7 @@ func (dv *DimensionValue) add(s string) { // the final report. func emptyKindStateReport() *KindStateReport { return &KindStateReport{ - Kinds: make(map[string]kindsys.SomeKindProperties), + Kinds: make(map[string]Kind), Dimensions: map[string]Dimension{ "maturity": { "planned": emptyDimensionValue("planned"), @@ -117,9 +177,14 @@ func buildKindStateReport() *KindStateReport { seen := make(map[string]bool) for _, k := range b.All() { seen[k.Props().Common().Name] = true + k.Lineage() switch k.Props().(type) { case kindsys.CoreProperties: - r.add(k.Props(), "core") + r.add(Kind{ + SomeKindProperties: k.Props(), + Category: "core", + Links: buildCoreLinks(k.Lineage(), k.Decl().Properties), + }) } } @@ -128,25 +193,32 @@ func buildKindStateReport() *KindStateReport { continue } - r.add(kindsys.CoreProperties{ - CommonProperties: kindsys.CommonProperties{ - Name: kn, - PluralName: kn + "s", - MachineName: machinize(kn), - PluralMachineName: machinize(kn) + "s", - Maturity: "planned", + r.add(Kind{ + SomeKindProperties: kindsys.CoreProperties{ + CommonProperties: kindsys.CommonProperties{ + Name: kn, + PluralName: kn + "s", + MachineName: machinize(kn), + PluralMachineName: machinize(kn) + "s", + Maturity: "planned", + }, }, - }, "core") + Category: "core", + }) } all := kindsys.SchemaInterfaces(nil) for _, pp := range corelist.New(nil) { for _, si := range all { if ck, has := pp.ComposableKinds[si.Name()]; has { - r.add(ck.Props(), "composable") + r.add(Kind{ + SomeKindProperties: ck.Props(), + Category: "composable", + Links: buildComposableLinks(pp.Properties, ck.Decl().Properties), + }) } else if may := si.Should(string(pp.Properties.Type)); may { n := plugindef.DerivePascalName(pp.Properties) + si.Name() - props := kindsys.ComposableProperties{ + ck := kindsys.ComposableProperties{ SchemaInterface: si.Name(), CommonProperties: kindsys.CommonProperties{ Name: n, @@ -157,7 +229,10 @@ func buildKindStateReport() *KindStateReport { Maturity: "planned", }, } - r.add(props, "composable") + r.add(Kind{ + SomeKindProperties: ck, + Category: "composable", + }) } } } @@ -171,6 +246,66 @@ func buildKindStateReport() *KindStateReport { return r } +func buildDocsRef(category string, props kindsys.CommonProperties) string { + return path.Join(docsBaseURL, category, props.MachineName, "schema-reference") +} + +func buildCoreLinks(lin thema.Lineage, cp kindsys.CoreProperties) KindLinks { + const category = "core" + vpath := fmt.Sprintf("v%v", lin.Latest().Version()[0]) + if cp.Maturity.Less(kindsys.MaturityStable) { + vpath = "x" + } + + return KindLinks{ + Schema: path.Join(repoBaseURL, fmt.Sprintf(coreCUEPath, cp.MachineName, cp.MachineName)), + Go: path.Join(repoBaseURL, fmt.Sprintf(coreGoPath, cp.MachineName)), + Ts: path.Join(repoBaseURL, fmt.Sprintf(coreTSPath, cp.MachineName, vpath, cp.MachineName)), + Docs: path.Join(docsBaseURL, category, cp.MachineName, "schema-reference"), + } +} + +// used to map names for those plugins that aren't following +// naming conventions, like 'annonlist' which comes from "Annotations list". +var irregularPluginNames = map[string]string{ + // Panel + "alertgroups": "alertGroups", + "annotationslist": "annolist", + "dashboardlist": "dashlist", + "nodegraph": "nodeGraph", + "statetimeline": "state-timeline", + "statushistory": "status-history", + "tableold": "table-old", + // Datasource + "googlecloudmonitoring": "cloud-monitoring", + "azuremonitor": "grafana-azure-monitor-datasource", + "microsoftsqlserver": "mssql", + "postgresql": "postgres", + "testdatadb": "testdata", +} + +func buildComposableLinks(pp plugindef.PluginDef, cp kindsys.ComposableProperties) KindLinks { + const category = "composable" + schemaInterface := strings.ToLower(cp.SchemaInterface) + + pName := strings.Replace(cp.MachineName, schemaInterface, "", 1) + if irr, ok := irregularPluginNames[pName]; ok { + pName = irr + } + + var goLink string + if pp.Backend != nil && *pp.Backend { + goLink = path.Join(repoBaseURL, fmt.Sprintf(composableGoPath, pName, schemaInterface, schemaInterface)) + } + + return KindLinks{ + Schema: path.Join(repoBaseURL, fmt.Sprintf(composableCUEPath, string(pp.Type), pName, schemaInterface)), + Go: goLink, + Ts: path.Join(repoBaseURL, fmt.Sprintf(composableTSPath, string(pp.Type), pName, schemaInterface)), + Docs: path.Join(docsBaseURL, category, cp.MachineName, "schema-reference"), + } +} + func machinize(s string) string { return strings.Map(func(r rune) rune { switch { @@ -190,6 +325,10 @@ func machinize(s string) string { }, s) } +func toCamelCase(s string) string { + return strings.ToLower(string(s[0])) + s[1:] +} + type reportJenny struct{} func (reportJenny) JennyName() string { diff --git a/pkg/kindsys/report.json b/pkg/kindsys/report.json index ac2f97db3a5..b7d2378d7dd 100644 --- a/pkg/kindsys/report.json +++ b/pkg/kindsys/report.json @@ -1,954 +1,1472 @@ { "kinds": { "alertgroupspanelcfg": { - "name": "AlertGroupsPanelCfg", - "pluralName": "AlertGroupsPanelCfgs", - "machineName": "alertgroupspanelcfg", - "pluralMachineName": "alertgroupspanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "alertgroupspanelcfg", + "maturity": "planned", + "name": "AlertGroupsPanelCfg", + "pluralMachineName": "alertgroupspanelcfgs", + "pluralName": "AlertGroupsPanelCfgs", "schemaInterface": "PanelCfg" }, "alertlistpanelcfg": { - "name": "AlertListPanelCfg", - "pluralName": "AlertListPanelCfgs", - "machineName": "alertlistpanelcfg", - "pluralMachineName": "alertlistpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "alertlistpanelcfg", + "maturity": "planned", + "name": "AlertListPanelCfg", + "pluralMachineName": "alertlistpanelcfgs", + "pluralName": "AlertListPanelCfgs", "schemaInterface": "PanelCfg" }, "alertmanagerdataquery": { - "name": "AlertmanagerDataQuery", - "pluralName": "AlertmanagerDataQuerys", - "machineName": "alertmanagerdataquery", - "pluralMachineName": "alertmanagerdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "alertmanagerdataquery", + "maturity": "planned", + "name": "AlertmanagerDataQuery", + "pluralMachineName": "alertmanagerdataquerys", + "pluralName": "AlertmanagerDataQuerys", "schemaInterface": "DataQuery" }, "alertmanagerdatasourcecfg": { - "name": "AlertmanagerDataSourceCfg", - "pluralName": "AlertmanagerDataSourceCfgs", - "machineName": "alertmanagerdatasourcecfg", - "pluralMachineName": "alertmanagerdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "alertmanagerdatasourcecfg", + "maturity": "planned", + "name": "AlertmanagerDataSourceCfg", + "pluralMachineName": "alertmanagerdatasourcecfgs", + "pluralName": "AlertmanagerDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "annotationslistpanelcfg": { - "name": "AnnotationsListPanelCfg", - "pluralName": "AnnotationsListPanelCfgs", - "machineName": "annotationslistpanelcfg", - "pluralMachineName": "annotationslistpanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/annotationslistpanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/annolist/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/annolist/panelcfg.gen.ts" + }, + "machineName": "annotationslistpanelcfg", + "maturity": "experimental", + "name": "AnnotationsListPanelCfg", + "pluralMachineName": "annotationslistpanelcfgs", + "pluralName": "AnnotationsListPanelCfgs", "schemaInterface": "PanelCfg" }, "apikey": { - "name": "APIKey", - "pluralName": "APIKeys", - "machineName": "apikey", - "pluralMachineName": "apikeys", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "azuremonitordataquery": { - "name": "AzureMonitorDataQuery", - "pluralName": "AzureMonitorDataQuerys", - "machineName": "azuremonitordataquery", - "pluralMachineName": "azuremonitordataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "apikey", + "maturity": "planned", + "name": "APIKey", + "pluralMachineName": "apikeys", + "pluralName": "APIKeys" + }, + "azuremonitordataquery": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "azuremonitordataquery", + "maturity": "planned", + "name": "AzureMonitorDataQuery", + "pluralMachineName": "azuremonitordataquerys", + "pluralName": "AzureMonitorDataQuerys", "schemaInterface": "DataQuery" }, "azuremonitordatasourcecfg": { - "name": "AzureMonitorDataSourceCfg", - "pluralName": "AzureMonitorDataSourceCfgs", - "machineName": "azuremonitordatasourcecfg", - "pluralMachineName": "azuremonitordatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "azuremonitordatasourcecfg", + "maturity": "planned", + "name": "AzureMonitorDataSourceCfg", + "pluralMachineName": "azuremonitordatasourcecfgs", + "pluralName": "AzureMonitorDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "barchartpanelcfg": { - "name": "BarChartPanelCfg", - "pluralName": "BarChartPanelCfgs", - "machineName": "barchartpanelcfg", - "pluralMachineName": "barchartpanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/barchartpanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/barchart/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/barchart/panelcfg.gen.ts" + }, + "machineName": "barchartpanelcfg", + "maturity": "experimental", + "name": "BarChartPanelCfg", + "pluralMachineName": "barchartpanelcfgs", + "pluralName": "BarChartPanelCfgs", "schemaInterface": "PanelCfg" }, "bargaugepanelcfg": { - "name": "BarGaugePanelCfg", - "pluralName": "BarGaugePanelCfgs", - "machineName": "bargaugepanelcfg", - "pluralMachineName": "bargaugepanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/bargaugepanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/bargauge/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/bargauge/panelcfg.gen.ts" + }, + "machineName": "bargaugepanelcfg", + "maturity": "experimental", + "name": "BarGaugePanelCfg", + "pluralMachineName": "bargaugepanelcfgs", + "pluralName": "BarGaugePanelCfgs", "schemaInterface": "PanelCfg" }, "cloudwatchdataquery": { - "name": "CloudWatchDataQuery", - "pluralName": "CloudWatchDataQuerys", - "machineName": "cloudwatchdataquery", - "pluralMachineName": "cloudwatchdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "cloudwatchdataquery", + "maturity": "planned", + "name": "CloudWatchDataQuery", + "pluralMachineName": "cloudwatchdataquerys", + "pluralName": "CloudWatchDataQuerys", "schemaInterface": "DataQuery" }, "cloudwatchdatasourcecfg": { - "name": "CloudWatchDataSourceCfg", - "pluralName": "CloudWatchDataSourceCfgs", - "machineName": "cloudwatchdatasourcecfg", - "pluralMachineName": "cloudwatchdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "cloudwatchdatasourcecfg", + "maturity": "planned", + "name": "CloudWatchDataSourceCfg", + "pluralMachineName": "cloudwatchdatasourcecfgs", + "pluralName": "CloudWatchDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "dashboard": { - "name": "Dashboard", - "pluralName": "Dashboards", - "machineName": "dashboard", - "pluralMachineName": "dashboards", - "lineageIsGroup": false, - "maturity": "experimental", - "currentVersion": [ - 0, - 0 - ] - }, - "dashboarddataquery": { - "name": "DashboardDataQuery", - "pluralName": "DashboardDataQuerys", - "machineName": "dashboarddataquery", - "pluralMachineName": "dashboarddataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/core/dashboard/schema-reference", + "go": "https:/github.com/grafana/grafana/tree/main/pkg/kinds/dashboard", + "schema": "https:/github.com/grafana/grafana/tree/main/kinds/dashboard/dashboard_kind.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts" + }, + "machineName": "dashboard", + "maturity": "experimental", + "name": "Dashboard", + "pluralMachineName": "dashboards", + "pluralName": "Dashboards" + }, + "dashboarddataquery": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "dashboarddataquery", + "maturity": "planned", + "name": "DashboardDataQuery", + "pluralMachineName": "dashboarddataquerys", + "pluralName": "DashboardDataQuerys", "schemaInterface": "DataQuery" }, "dashboarddatasourcecfg": { - "name": "DashboardDataSourceCfg", - "pluralName": "DashboardDataSourceCfgs", - "machineName": "dashboarddatasourcecfg", - "pluralMachineName": "dashboarddatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "dashboarddatasourcecfg", + "maturity": "planned", + "name": "DashboardDataSourceCfg", + "pluralMachineName": "dashboarddatasourcecfgs", + "pluralName": "DashboardDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "dashboardlistpanelcfg": { - "name": "DashboardListPanelCfg", - "pluralName": "DashboardListPanelCfgs", - "machineName": "dashboardlistpanelcfg", - "pluralMachineName": "dashboardlistpanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/dashboardlistpanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/dashlist/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/dashlist/panelcfg.gen.ts" + }, + "machineName": "dashboardlistpanelcfg", + "maturity": "experimental", + "name": "DashboardListPanelCfg", + "pluralMachineName": "dashboardlistpanelcfgs", + "pluralName": "DashboardListPanelCfgs", "schemaInterface": "PanelCfg" }, "datasource": { - "name": "DataSource", - "pluralName": "DataSources", - "machineName": "datasource", - "pluralMachineName": "datasources", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "debugpanelcfg": { - "name": "DebugPanelCfg", - "pluralName": "DebugPanelCfgs", - "machineName": "debugpanelcfg", - "pluralMachineName": "debugpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "datasource", + "maturity": "planned", + "name": "DataSource", + "pluralMachineName": "datasources", + "pluralName": "DataSources" + }, + "debugpanelcfg": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "debugpanelcfg", + "maturity": "planned", + "name": "DebugPanelCfg", + "pluralMachineName": "debugpanelcfgs", + "pluralName": "DebugPanelCfgs", "schemaInterface": "PanelCfg" }, "elasticsearchdataquery": { - "name": "ElasticsearchDataQuery", - "pluralName": "ElasticsearchDataQuerys", - "machineName": "elasticsearchdataquery", - "pluralMachineName": "elasticsearchdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "elasticsearchdataquery", + "maturity": "planned", + "name": "ElasticsearchDataQuery", + "pluralMachineName": "elasticsearchdataquerys", + "pluralName": "ElasticsearchDataQuerys", "schemaInterface": "DataQuery" }, "elasticsearchdatasourcecfg": { - "name": "ElasticsearchDataSourceCfg", - "pluralName": "ElasticsearchDataSourceCfgs", - "machineName": "elasticsearchdatasourcecfg", - "pluralMachineName": "elasticsearchdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "elasticsearchdatasourcecfg", + "maturity": "planned", + "name": "ElasticsearchDataSourceCfg", + "pluralMachineName": "elasticsearchdatasourcecfgs", + "pluralName": "ElasticsearchDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "flamegraphpanelcfg": { - "name": "FlameGraphPanelCfg", - "pluralName": "FlameGraphPanelCfgs", - "machineName": "flamegraphpanelcfg", - "pluralMachineName": "flamegraphpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "flamegraphpanelcfg", + "maturity": "planned", + "name": "FlameGraphPanelCfg", + "pluralMachineName": "flamegraphpanelcfgs", + "pluralName": "FlameGraphPanelCfgs", "schemaInterface": "PanelCfg" }, "folder": { - "name": "Folder", - "pluralName": "Folders", - "machineName": "folder", - "pluralMachineName": "folders", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "gaugepanelcfg": { - "name": "GaugePanelCfg", - "pluralName": "GaugePanelCfgs", - "machineName": "gaugepanelcfg", - "pluralMachineName": "gaugepanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "folder", + "maturity": "planned", + "name": "Folder", + "pluralMachineName": "folders", + "pluralName": "Folders" + }, + "gaugepanelcfg": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/gaugepanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/gauge/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/gauge/panelcfg.gen.ts" + }, + "machineName": "gaugepanelcfg", + "maturity": "experimental", + "name": "GaugePanelCfg", + "pluralMachineName": "gaugepanelcfgs", + "pluralName": "GaugePanelCfgs", "schemaInterface": "PanelCfg" }, "geomappanelcfg": { - "name": "GeomapPanelCfg", - "pluralName": "GeomapPanelCfgs", - "machineName": "geomappanelcfg", - "pluralMachineName": "geomappanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "geomappanelcfg", + "maturity": "planned", + "name": "GeomapPanelCfg", + "pluralMachineName": "geomappanelcfgs", + "pluralName": "GeomapPanelCfgs", "schemaInterface": "PanelCfg" }, "gettingstartedpanelcfg": { - "name": "GettingStartedPanelCfg", - "pluralName": "GettingStartedPanelCfgs", - "machineName": "gettingstartedpanelcfg", - "pluralMachineName": "gettingstartedpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "gettingstartedpanelcfg", + "maturity": "planned", + "name": "GettingStartedPanelCfg", + "pluralMachineName": "gettingstartedpanelcfgs", + "pluralName": "GettingStartedPanelCfgs", "schemaInterface": "PanelCfg" }, "googlecloudmonitoringdataquery": { - "name": "GoogleCloudMonitoringDataQuery", - "pluralName": "GoogleCloudMonitoringDataQuerys", - "machineName": "googlecloudmonitoringdataquery", - "pluralMachineName": "googlecloudmonitoringdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "googlecloudmonitoringdataquery", + "maturity": "planned", + "name": "GoogleCloudMonitoringDataQuery", + "pluralMachineName": "googlecloudmonitoringdataquerys", + "pluralName": "GoogleCloudMonitoringDataQuerys", "schemaInterface": "DataQuery" }, "googlecloudmonitoringdatasourcecfg": { - "name": "GoogleCloudMonitoringDataSourceCfg", - "pluralName": "GoogleCloudMonitoringDataSourceCfgs", - "machineName": "googlecloudmonitoringdatasourcecfg", - "pluralMachineName": "googlecloudmonitoringdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "googlecloudmonitoringdatasourcecfg", + "maturity": "planned", + "name": "GoogleCloudMonitoringDataSourceCfg", + "pluralMachineName": "googlecloudmonitoringdatasourcecfgs", + "pluralName": "GoogleCloudMonitoringDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "grafanadataquery": { - "name": "GrafanaDataQuery", - "pluralName": "GrafanaDataQuerys", - "machineName": "grafanadataquery", - "pluralMachineName": "grafanadataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "grafanadataquery", + "maturity": "planned", + "name": "GrafanaDataQuery", + "pluralMachineName": "grafanadataquerys", + "pluralName": "GrafanaDataQuerys", "schemaInterface": "DataQuery" }, "grafanadatasourcecfg": { - "name": "GrafanaDataSourceCfg", - "pluralName": "GrafanaDataSourceCfgs", - "machineName": "grafanadatasourcecfg", - "pluralMachineName": "grafanadatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "grafanadatasourcecfg", + "maturity": "planned", + "name": "GrafanaDataSourceCfg", + "pluralMachineName": "grafanadatasourcecfgs", + "pluralName": "GrafanaDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "graphitedataquery": { - "name": "GraphiteDataQuery", - "pluralName": "GraphiteDataQuerys", - "machineName": "graphitedataquery", - "pluralMachineName": "graphitedataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "graphitedataquery", + "maturity": "planned", + "name": "GraphiteDataQuery", + "pluralMachineName": "graphitedataquerys", + "pluralName": "GraphiteDataQuerys", "schemaInterface": "DataQuery" }, "graphitedatasourcecfg": { - "name": "GraphiteDataSourceCfg", - "pluralName": "GraphiteDataSourceCfgs", - "machineName": "graphitedatasourcecfg", - "pluralMachineName": "graphitedatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "graphitedatasourcecfg", + "maturity": "planned", + "name": "GraphiteDataSourceCfg", + "pluralMachineName": "graphitedatasourcecfgs", + "pluralName": "GraphiteDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "grapholdpanelcfg": { - "name": "GraphOldPanelCfg", - "pluralName": "GraphOldPanelCfgs", - "machineName": "grapholdpanelcfg", - "pluralMachineName": "grapholdpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "grapholdpanelcfg", + "maturity": "planned", + "name": "GraphOldPanelCfg", + "pluralMachineName": "grapholdpanelcfgs", + "pluralName": "GraphOldPanelCfgs", "schemaInterface": "PanelCfg" }, "histogrampanelcfg": { - "name": "HistogramPanelCfg", - "pluralName": "HistogramPanelCfgs", - "machineName": "histogrampanelcfg", - "pluralMachineName": "histogrampanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/histogrampanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/histogram/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/histogram/panelcfg.gen.ts" + }, + "machineName": "histogrampanelcfg", + "maturity": "experimental", + "name": "HistogramPanelCfg", + "pluralMachineName": "histogrampanelcfgs", + "pluralName": "HistogramPanelCfgs", "schemaInterface": "PanelCfg" }, "iconpanelcfg": { - "name": "IconPanelCfg", - "pluralName": "IconPanelCfgs", - "machineName": "iconpanelcfg", - "pluralMachineName": "iconpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "iconpanelcfg", + "maturity": "planned", + "name": "IconPanelCfg", + "pluralMachineName": "iconpanelcfgs", + "pluralName": "IconPanelCfgs", "schemaInterface": "PanelCfg" }, "jaegerdataquery": { - "name": "JaegerDataQuery", - "pluralName": "JaegerDataQuerys", - "machineName": "jaegerdataquery", - "pluralMachineName": "jaegerdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "jaegerdataquery", + "maturity": "planned", + "name": "JaegerDataQuery", + "pluralMachineName": "jaegerdataquerys", + "pluralName": "JaegerDataQuerys", "schemaInterface": "DataQuery" }, "jaegerdatasourcecfg": { - "name": "JaegerDataSourceCfg", - "pluralName": "JaegerDataSourceCfgs", - "machineName": "jaegerdatasourcecfg", - "pluralMachineName": "jaegerdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "jaegerdatasourcecfg", + "maturity": "planned", + "name": "JaegerDataSourceCfg", + "pluralMachineName": "jaegerdatasourcecfgs", + "pluralName": "JaegerDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "livepanelcfg": { - "name": "LivePanelCfg", - "pluralName": "LivePanelCfgs", - "machineName": "livepanelcfg", - "pluralMachineName": "livepanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "livepanelcfg", + "maturity": "planned", + "name": "LivePanelCfg", + "pluralMachineName": "livepanelcfgs", + "pluralName": "LivePanelCfgs", "schemaInterface": "PanelCfg" }, "logspanelcfg": { - "name": "LogsPanelCfg", - "pluralName": "LogsPanelCfgs", - "machineName": "logspanelcfg", - "pluralMachineName": "logspanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "logspanelcfg", + "maturity": "planned", + "name": "LogsPanelCfg", + "pluralMachineName": "logspanelcfgs", + "pluralName": "LogsPanelCfgs", "schemaInterface": "PanelCfg" }, "lokidataquery": { - "name": "LokiDataQuery", - "pluralName": "LokiDataQuerys", - "machineName": "lokidataquery", - "pluralMachineName": "lokidataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "lokidataquery", + "maturity": "planned", + "name": "LokiDataQuery", + "pluralMachineName": "lokidataquerys", + "pluralName": "LokiDataQuerys", "schemaInterface": "DataQuery" }, "lokidatasourcecfg": { - "name": "LokiDataSourceCfg", - "pluralName": "LokiDataSourceCfgs", - "machineName": "lokidatasourcecfg", - "pluralMachineName": "lokidatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "lokidatasourcecfg", + "maturity": "planned", + "name": "LokiDataSourceCfg", + "pluralMachineName": "lokidatasourcecfgs", + "pluralName": "LokiDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "microsoftsqlserverdataquery": { - "name": "MicrosoftSQLServerDataQuery", - "pluralName": "MicrosoftSQLServerDataQuerys", - "machineName": "microsoftsqlserverdataquery", - "pluralMachineName": "microsoftsqlserverdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "microsoftsqlserverdataquery", + "maturity": "planned", + "name": "MicrosoftSQLServerDataQuery", + "pluralMachineName": "microsoftsqlserverdataquerys", + "pluralName": "MicrosoftSQLServerDataQuerys", "schemaInterface": "DataQuery" }, "microsoftsqlserverdatasourcecfg": { - "name": "MicrosoftSQLServerDataSourceCfg", - "pluralName": "MicrosoftSQLServerDataSourceCfgs", - "machineName": "microsoftsqlserverdatasourcecfg", - "pluralMachineName": "microsoftsqlserverdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "microsoftsqlserverdatasourcecfg", + "maturity": "planned", + "name": "MicrosoftSQLServerDataSourceCfg", + "pluralMachineName": "microsoftsqlserverdatasourcecfgs", + "pluralName": "MicrosoftSQLServerDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "mysqldataquery": { - "name": "MySQLDataQuery", - "pluralName": "MySQLDataQuerys", - "machineName": "mysqldataquery", - "pluralMachineName": "mysqldataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "mysqldataquery", + "maturity": "planned", + "name": "MySQLDataQuery", + "pluralMachineName": "mysqldataquerys", + "pluralName": "MySQLDataQuerys", "schemaInterface": "DataQuery" }, "mysqldatasourcecfg": { - "name": "MySQLDataSourceCfg", - "pluralName": "MySQLDataSourceCfgs", - "machineName": "mysqldatasourcecfg", - "pluralMachineName": "mysqldatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "mysqldatasourcecfg", + "maturity": "planned", + "name": "MySQLDataSourceCfg", + "pluralMachineName": "mysqldatasourcecfgs", + "pluralName": "MySQLDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "newspanelcfg": { - "name": "NewsPanelCfg", - "pluralName": "NewsPanelCfgs", - "machineName": "newspanelcfg", - "pluralMachineName": "newspanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/newspanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/news/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/news/panelcfg.gen.ts" + }, + "machineName": "newspanelcfg", + "maturity": "experimental", + "name": "NewsPanelCfg", + "pluralMachineName": "newspanelcfgs", + "pluralName": "NewsPanelCfgs", "schemaInterface": "PanelCfg" }, "nodegraphpanelcfg": { - "name": "NodeGraphPanelCfg", - "pluralName": "NodeGraphPanelCfgs", - "machineName": "nodegraphpanelcfg", - "pluralMachineName": "nodegraphpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "nodegraphpanelcfg", + "maturity": "planned", + "name": "NodeGraphPanelCfg", + "pluralMachineName": "nodegraphpanelcfgs", + "pluralName": "NodeGraphPanelCfgs", "schemaInterface": "PanelCfg" }, "parcadataquery": { - "name": "ParcaDataQuery", - "pluralName": "ParcaDataQuerys", - "machineName": "parcadataquery", - "pluralMachineName": "parcadataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "parcadataquery", + "maturity": "planned", + "name": "ParcaDataQuery", + "pluralMachineName": "parcadataquerys", + "pluralName": "ParcaDataQuerys", "schemaInterface": "DataQuery" }, "parcadatasourcecfg": { - "name": "ParcaDataSourceCfg", - "pluralName": "ParcaDataSourceCfgs", - "machineName": "parcadatasourcecfg", - "pluralMachineName": "parcadatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "parcadatasourcecfg", + "maturity": "planned", + "name": "ParcaDataSourceCfg", + "pluralMachineName": "parcadatasourcecfgs", + "pluralName": "ParcaDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "phlaredataquery": { - "name": "PhlareDataQuery", - "pluralName": "PhlareDataQuerys", - "machineName": "phlaredataquery", - "pluralMachineName": "phlaredataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "phlaredataquery", + "maturity": "planned", + "name": "PhlareDataQuery", + "pluralMachineName": "phlaredataquerys", + "pluralName": "PhlareDataQuerys", "schemaInterface": "DataQuery" }, "phlaredatasourcecfg": { - "name": "PhlareDataSourceCfg", - "pluralName": "PhlareDataSourceCfgs", - "machineName": "phlaredatasourcecfg", - "pluralMachineName": "phlaredatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "phlaredatasourcecfg", + "maturity": "planned", + "name": "PhlareDataSourceCfg", + "pluralMachineName": "phlaredatasourcecfgs", + "pluralName": "PhlareDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "piechartpanelcfg": { - "name": "PieChartPanelCfg", - "pluralName": "PieChartPanelCfgs", - "machineName": "piechartpanelcfg", - "pluralMachineName": "piechartpanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/piechartpanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/piechart/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/piechart/panelcfg.gen.ts" + }, + "machineName": "piechartpanelcfg", + "maturity": "experimental", + "name": "PieChartPanelCfg", + "pluralMachineName": "piechartpanelcfgs", + "pluralName": "PieChartPanelCfgs", "schemaInterface": "PanelCfg" }, "playlist": { - "name": "Playlist", - "pluralName": "Playlists", - "machineName": "playlist", - "pluralMachineName": "playlists", - "lineageIsGroup": false, - "maturity": "merged", - "currentVersion": [ - 0, - 0 - ] - }, - "postgresqldataquery": { - "name": "PostgreSQLDataQuery", - "pluralName": "PostgreSQLDataQuerys", - "machineName": "postgresqldataquery", - "pluralMachineName": "postgresqldataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/core/playlist/schema-reference", + "go": "https:/github.com/grafana/grafana/tree/main/pkg/kinds/playlist", + "schema": "https:/github.com/grafana/grafana/tree/main/kinds/playlist/playlist_kind.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/packages/grafana-schema/src/raw/playlist/x/playlist_types.gen.ts" + }, + "machineName": "playlist", + "maturity": "merged", + "name": "Playlist", + "pluralMachineName": "playlists", + "pluralName": "Playlists" + }, + "postgresqldataquery": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "postgresqldataquery", + "maturity": "planned", + "name": "PostgreSQLDataQuery", + "pluralMachineName": "postgresqldataquerys", + "pluralName": "PostgreSQLDataQuerys", "schemaInterface": "DataQuery" }, "postgresqldatasourcecfg": { - "name": "PostgreSQLDataSourceCfg", - "pluralName": "PostgreSQLDataSourceCfgs", - "machineName": "postgresqldatasourcecfg", - "pluralMachineName": "postgresqldatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "postgresqldatasourcecfg", + "maturity": "planned", + "name": "PostgreSQLDataSourceCfg", + "pluralMachineName": "postgresqldatasourcecfgs", + "pluralName": "PostgreSQLDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "prometheusdataquery": { - "name": "PrometheusDataQuery", - "pluralName": "PrometheusDataQuerys", - "machineName": "prometheusdataquery", - "pluralMachineName": "prometheusdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "prometheusdataquery", + "maturity": "planned", + "name": "PrometheusDataQuery", + "pluralMachineName": "prometheusdataquerys", + "pluralName": "PrometheusDataQuerys", "schemaInterface": "DataQuery" }, "prometheusdatasourcecfg": { - "name": "PrometheusDataSourceCfg", - "pluralName": "PrometheusDataSourceCfgs", - "machineName": "prometheusdatasourcecfg", - "pluralMachineName": "prometheusdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "prometheusdatasourcecfg", + "maturity": "planned", + "name": "PrometheusDataSourceCfg", + "pluralMachineName": "prometheusdatasourcecfgs", + "pluralName": "PrometheusDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "query": { - "name": "Query", - "pluralName": "Querys", - "machineName": "query", - "pluralMachineName": "querys", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "queryhistory": { - "name": "QueryHistory", - "pluralName": "QueryHistorys", - "machineName": "queryhistory", - "pluralMachineName": "queryhistorys", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "serviceaccount": { - "name": "ServiceAccount", - "pluralName": "ServiceAccounts", - "machineName": "serviceaccount", - "pluralMachineName": "serviceaccounts", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "statpanelcfg": { - "name": "StatPanelCfg", - "pluralName": "StatPanelCfgs", - "machineName": "statpanelcfg", - "pluralMachineName": "statpanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "query", + "maturity": "planned", + "name": "Query", + "pluralMachineName": "querys", + "pluralName": "Querys" + }, + "queryhistory": { + "category": "core", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "queryhistory", + "maturity": "planned", + "name": "QueryHistory", + "pluralMachineName": "queryhistorys", + "pluralName": "QueryHistorys" + }, + "serviceaccount": { + "category": "core", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "serviceaccount", + "maturity": "planned", + "name": "ServiceAccount", + "pluralMachineName": "serviceaccounts", + "pluralName": "ServiceAccounts" + }, + "statpanelcfg": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/statpanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/stat/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/stat/panelcfg.gen.ts" + }, + "machineName": "statpanelcfg", + "maturity": "experimental", + "name": "StatPanelCfg", + "pluralMachineName": "statpanelcfgs", + "pluralName": "StatPanelCfgs", "schemaInterface": "PanelCfg" }, "tableoldpanelcfg": { - "name": "TableOldPanelCfg", - "pluralName": "TableOldPanelCfgs", - "machineName": "tableoldpanelcfg", - "pluralMachineName": "tableoldpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "tableoldpanelcfg", + "maturity": "planned", + "name": "TableOldPanelCfg", + "pluralMachineName": "tableoldpanelcfgs", + "pluralName": "TableOldPanelCfgs", "schemaInterface": "PanelCfg" }, "team": { - "name": "Team", - "pluralName": "Teams", - "machineName": "team", - "pluralMachineName": "teams", - "lineageIsGroup": false, - "maturity": "merged", - "currentVersion": [ - 0, - 0 - ] - }, - "tempodataquery": { - "name": "TempoDataQuery", - "pluralName": "TempoDataQuerys", - "machineName": "tempodataquery", - "pluralMachineName": "tempodataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/core/team/schema-reference", + "go": "https:/github.com/grafana/grafana/tree/main/pkg/kinds/team", + "schema": "https:/github.com/grafana/grafana/tree/main/kinds/team/team_kind.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/packages/grafana-schema/src/raw/team/x/team_types.gen.ts" + }, + "machineName": "team", + "maturity": "merged", + "name": "Team", + "pluralMachineName": "teams", + "pluralName": "Teams" + }, + "tempodataquery": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "tempodataquery", + "maturity": "planned", + "name": "TempoDataQuery", + "pluralMachineName": "tempodataquerys", + "pluralName": "TempoDataQuerys", "schemaInterface": "DataQuery" }, "tempodatasourcecfg": { - "name": "TempoDataSourceCfg", - "pluralName": "TempoDataSourceCfgs", - "machineName": "tempodatasourcecfg", - "pluralMachineName": "tempodatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "tempodatasourcecfg", + "maturity": "planned", + "name": "TempoDataSourceCfg", + "pluralMachineName": "tempodatasourcecfgs", + "pluralName": "TempoDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "testdatadbdataquery": { - "name": "TestDataDBDataQuery", - "pluralName": "TestDataDBDataQuerys", - "machineName": "testdatadbdataquery", - "pluralMachineName": "testdatadbdataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "testdatadbdataquery", + "maturity": "planned", + "name": "TestDataDBDataQuery", + "pluralMachineName": "testdatadbdataquerys", + "pluralName": "TestDataDBDataQuerys", "schemaInterface": "DataQuery" }, "testdatadbdatasourcecfg": { - "name": "TestDataDBDataSourceCfg", - "pluralName": "TestDataDBDataSourceCfgs", - "machineName": "testdatadbdatasourcecfg", - "pluralMachineName": "testdatadbdatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "testdatadbdatasourcecfg", + "maturity": "planned", + "name": "TestDataDBDataSourceCfg", + "pluralMachineName": "testdatadbdatasourcecfgs", + "pluralName": "TestDataDBDataSourceCfgs", "schemaInterface": "DataSourceCfg" }, "textpanelcfg": { - "name": "TextPanelCfg", - "pluralName": "TextPanelCfgs", - "machineName": "textpanelcfg", - "pluralMachineName": "textpanelcfgs", - "lineageIsGroup": true, - "maturity": "experimental", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/textpanelcfg/schema-reference", + "go": "n/a", + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/text/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/text/panelcfg.gen.ts" + }, + "machineName": "textpanelcfg", + "maturity": "experimental", + "name": "TextPanelCfg", + "pluralMachineName": "textpanelcfgs", + "pluralName": "TextPanelCfgs", "schemaInterface": "PanelCfg" }, "thumb": { - "name": "Thumb", - "pluralName": "Thumbs", - "machineName": "thumb", - "pluralMachineName": "thumbs", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "tracespanelcfg": { - "name": "TracesPanelCfg", - "pluralName": "TracesPanelCfgs", - "machineName": "tracespanelcfg", - "pluralMachineName": "tracespanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "thumb", + "maturity": "planned", + "name": "Thumb", + "pluralMachineName": "thumbs", + "pluralName": "Thumbs" + }, + "tracespanelcfg": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "tracespanelcfg", + "maturity": "planned", + "name": "TracesPanelCfg", + "pluralMachineName": "tracespanelcfgs", + "pluralName": "TracesPanelCfgs", "schemaInterface": "PanelCfg" }, "user": { - "name": "User", - "pluralName": "Users", - "machineName": "user", - "pluralMachineName": "users", - "lineageIsGroup": false, - "maturity": "planned", - "currentVersion": [ - 0, - 0 - ] - }, - "welcomepanelcfg": { - "name": "WelcomePanelCfg", - "pluralName": "WelcomePanelCfgs", - "machineName": "welcomepanelcfg", - "pluralMachineName": "welcomepanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "core", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "user", + "maturity": "planned", + "name": "User", + "pluralMachineName": "users", + "pluralName": "Users" + }, + "welcomepanelcfg": { + "category": "composable", + "currentVersion": [ + 0, + 0 + ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "welcomepanelcfg", + "maturity": "planned", + "name": "WelcomePanelCfg", + "pluralMachineName": "welcomepanelcfgs", + "pluralName": "WelcomePanelCfgs", "schemaInterface": "PanelCfg" }, "xychartpanelcfg": { - "name": "XYChartPanelCfg", - "pluralName": "XYChartPanelCfgs", - "machineName": "xychartpanelcfg", - "pluralMachineName": "xychartpanelcfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "xychartpanelcfg", + "maturity": "planned", + "name": "XYChartPanelCfg", + "pluralMachineName": "xychartpanelcfgs", + "pluralName": "XYChartPanelCfgs", "schemaInterface": "PanelCfg" }, "zipkindataquery": { - "name": "ZipkinDataQuery", - "pluralName": "ZipkinDataQuerys", - "machineName": "zipkindataquery", - "pluralMachineName": "zipkindataquerys", - "lineageIsGroup": false, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": false, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "zipkindataquery", + "maturity": "planned", + "name": "ZipkinDataQuery", + "pluralMachineName": "zipkindataquerys", + "pluralName": "ZipkinDataQuerys", "schemaInterface": "DataQuery" }, "zipkindatasourcecfg": { - "name": "ZipkinDataSourceCfg", - "pluralName": "ZipkinDataSourceCfgs", - "machineName": "zipkindatasourcecfg", - "pluralMachineName": "zipkindatasourcecfgs", - "lineageIsGroup": true, - "maturity": "planned", + "category": "composable", "currentVersion": [ 0, 0 ], + "lineageIsGroup": true, + "links": { + "docs": "n/a", + "go": "n/a", + "schema": "n/a", + "ts": "n/a" + }, + "machineName": "zipkindatasourcecfg", + "maturity": "planned", + "name": "ZipkinDataSourceCfg", + "pluralMachineName": "zipkindatasourcecfgs", + "pluralName": "ZipkinDataSourceCfgs", "schemaInterface": "DataSourceCfg" } }, From 7ebbd016882383c63051e8ba944a39a1ba0e6817 Mon Sep 17 00:00:00 2001 From: Timur Olzhabayev Date: Tue, 24 Jan 2023 08:37:56 +0100 Subject: [PATCH 19/46] Linking criteria and adding file and env variables access (#61830) * Linking criteria and adding file and env variables access * Formatting * Update docs/sources/developers/plugins/publish-a-plugin.md Co-authored-by: Marcus Efraimsson Co-authored-by: Marcus Efraimsson --- docs/sources/developers/plugins/publish-a-plugin.md | 1 + .../developers/plugins/publishing-and-signing-criteria.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/sources/developers/plugins/publish-a-plugin.md b/docs/sources/developers/plugins/publish-a-plugin.md index ca8092e53d1..3e3b51f3191 100644 --- a/docs/sources/developers/plugins/publish-a-plugin.md +++ b/docs/sources/developers/plugins/publish-a-plugin.md @@ -86,6 +86,7 @@ Before you submit your plugin, we ask that you read our guidelines and frequentl To speed up the time it takes to review your plugin: +- Get familiar with our plugin [publishing and signing criteria](publishing-and-signing-criteria.md) - Check that your plugin is ready for review using the [plugin validator](https://github.com/grafana/plugin-validator). - Read our [6 tips for improving your Grafana plugin before you publish](https://grafana.com/blog/2021/01/21/6-tips-for-improving-your-grafana-plugin-before-you-publish/). - Refer to [plugin-examples](https://github.com/grafana/grafana-plugin-examples) to review best practices for building your plugin. diff --git a/docs/sources/developers/plugins/publishing-and-signing-criteria.md b/docs/sources/developers/plugins/publishing-and-signing-criteria.md index 948ce45d10f..277ba504c2f 100644 --- a/docs/sources/developers/plugins/publishing-and-signing-criteria.md +++ b/docs/sources/developers/plugins/publishing-and-signing-criteria.md @@ -19,6 +19,9 @@ Grafana plugins must adhere to the following criteria when being reviewed for pu - Abuse: plugins should not perform actions beyond the scope of the intended use. - Do not include hidden files - Do not manipulate the underlying environment, privileges, or related processes +- Security: + - Should not access the filesystem + - Should not access environment variables ## Commercial From 3b73b1624559b39aac27448df2ab8317c65098ba Mon Sep 17 00:00:00 2001 From: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com> Date: Tue, 24 Jan 2023 09:16:21 +0100 Subject: [PATCH 20/46] Alerting: Add maxdatapoints in alert rule form (#61904) Add maxdatapoints in alert rule form --- .../components/rule-editor/QueryRows.tsx | 18 +++- .../components/rule-editor/QueryWrapper.tsx | 83 ++++++++++++++++++- .../query-and-alert-condition/reducer.ts | 16 +++- 3 files changed, 112 insertions(+), 5 deletions(-) diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx index a8a96dfacc1..60acdd0011a 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx @@ -9,7 +9,7 @@ import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOp import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { AlertDataQuery, AlertQuery } from 'app/types/unified-alerting-dto'; -import { EmptyQueryWrapper, QueryWrapper } from './QueryWrapper'; +import { AlertQueryOptions, EmptyQueryWrapper, QueryWrapper } from './QueryWrapper'; import { errorFromSeries, getThresholdsForQueries } from './util'; interface Props { @@ -51,6 +51,21 @@ export class QueryRows extends PureComponent { ); }; + onChangeQueryOptions = (options: AlertQueryOptions, index: number) => { + const { queries, onQueriesChange } = this.props; + onQueriesChange( + queries.map((item, itemIndex) => { + if (itemIndex !== index) { + return item; + } + return { + ...item, + model: { ...item.model, maxDataPoints: options.maxDataPoints }, + }; + }) + ); + }; + onChangeDataSource = (settings: DataSourceInstanceSettings, index: number) => { const { queries, onQueriesChange } = this.props; @@ -170,6 +185,7 @@ export class QueryRows extends PureComponent { onChangeDataSource={this.onChangeDataSource} onDuplicateQuery={this.props.onDuplicateQuery} onChangeTimeRange={this.onChangeTimeRange} + onChangeQueryOptions={this.onChangeQueryOptions} thresholds={thresholdByRefId[query.refId]?.config} thresholdsType={thresholdByRefId[query.refId]?.mode} onRunQueries={this.props.onRunQueries} diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx index a373e91ffee..37154731ce3 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryWrapper.tsx @@ -1,6 +1,6 @@ import { css } from '@emotion/css'; import { cloneDeep } from 'lodash'; -import React, { FC, useState } from 'react'; +import React, { ChangeEvent, FC, useState } from 'react'; import { CoreApp, @@ -14,7 +14,15 @@ import { ThresholdsConfig, } from '@grafana/data'; import { Stack } from '@grafana/experimental'; -import { RelativeTimeRangePicker, useStyles2, Tooltip, Icon, GraphTresholdsStyleMode } from '@grafana/ui'; +import { + GraphTresholdsStyleMode, + Icon, + InlineFormLabel, + Input, + RelativeTimeRangePicker, + Tooltip, + useStyles2, +} from '@grafana/ui'; import { isExpressionQuery } from 'app/features/expressions/guards'; import { QueryEditorRow } from 'app/features/query/components/QueryEditorRow'; import { AlertQuery } from 'app/types/unified-alerting-dto'; @@ -25,6 +33,12 @@ import { AlertConditionIndicator } from '../expressions/AlertConditionIndicator' import { VizWrapper } from './VizWrapper'; +export const DEFAULT_MAX_DATA_POINTS = 43200; + +export interface AlertQueryOptions { + maxDataPoints?: number | undefined; +} + interface Props { data: PanelData; error?: Error; @@ -43,6 +57,7 @@ interface Props { onChangeThreshold?: (thresholds: ThresholdsConfig, index: number) => void; condition: string | null; onSetCondition: (refId: string) => void; + onChangeQueryOptions: (options: AlertQueryOptions, index: number) => void; } export const QueryWrapper: FC = ({ @@ -63,6 +78,7 @@ export const QueryWrapper: FC = ({ onChangeThreshold, condition, onSetCondition, + onChangeQueryOptions, }) => { const styles = useStyles2(getStyles); const isExpression = isExpressionQuery(query.model); @@ -96,11 +112,16 @@ export const QueryWrapper: FC = ({ // TODO add a warning label here too when the data looks like time series data and is used as an alert condition function HeaderExtras({ query, error, index }: { query: AlertQuery; error?: Error; index: number }) { + const queryOptions: AlertQueryOptions = { maxDataPoints: query.model.maxDataPoints }; + const alertQueryOptions: AlertQueryOptions = { + maxDataPoints: queryOptions.maxDataPoints, + }; + if (isExpressionQuery(query.model)) { return null; } else { return ( - + {onChangeTimeRange && ( = ({ onChange={(range) => onChangeTimeRange(range, index)} /> )} +
+ onChangeQueryOptions(options, index)} + /> +
onSetCondition(query.refId)} enabled={condition === query.refId} @@ -159,6 +186,53 @@ export const EmptyQueryWrapper = ({ children }: React.PropsWithChildren<{}>) => return
{children}
; }; +function MaxDataPointsOption({ + options, + onChange, +}: { + options: AlertQueryOptions; + onChange: (options: AlertQueryOptions) => void; +}) { + const value = options.maxDataPoints ?? ''; + + const onMaxDataPointsBlur = (event: ChangeEvent) => { + const maxDataPointsNumber = parseInt(event.target.value, 10); + + const maxDataPoints = isNaN(maxDataPointsNumber) || maxDataPointsNumber === 0 ? undefined : maxDataPointsNumber; + + if (maxDataPoints !== options.maxDataPoints) { + onChange({ + ...options, + maxDataPoints, + }); + } + }; + + return ( + + + The maximum data points per series. Used directly by some data sources and used in calculation of auto + interval. With streaming data this value is used for the rolling buffer. + + } + > + Max data points + + + + ); +} + const getStyles = (theme: GrafanaTheme2) => ({ wrapper: css` label: AlertingQueryWrapper; @@ -166,6 +240,9 @@ const getStyles = (theme: GrafanaTheme2) => ({ border: 1px solid ${theme.colors.border.medium}; border-radius: ${theme.shape.borderRadius(1)}; `, + queryOptions: css` + margin-bottom: -${theme.spacing(2)}; + `, dsTooltip: css` display: flex; align-items: center; diff --git a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/reducer.ts b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/reducer.ts index c4bdd8a2e77..fdd6c8396e8 100644 --- a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/reducer.ts +++ b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/reducer.ts @@ -1,6 +1,6 @@ import { createAction, createReducer } from '@reduxjs/toolkit'; -import { DataQuery, RelativeTimeRange, getDefaultRelativeTimeRange } from '@grafana/data'; +import { DataQuery, getDefaultRelativeTimeRange, RelativeTimeRange } from '@grafana/data'; import { getNextRefIdChar } from 'app/core/utils/query'; import { findDataSourceFromExpressionRecursive } from 'app/features/alerting/utils/dataSourceFromExpression'; import { @@ -43,6 +43,7 @@ export const updateExpressionRefId = createAction<{ oldRefId: string; newRefId: export const rewireExpressions = createAction<{ oldRefId: string; newRefId: string }>('rewireExpressions'); export const updateExpressionType = createAction<{ refId: string; type: ExpressionQueryType }>('updateExpressionType'); export const updateExpressionTimeRange = createAction('updateExpressionTimeRange'); +export const updateMaxDataPoints = createAction<{ refId: string; maxDataPoints: number }>('updateMaxDataPoints'); export const queriesAndExpressionsReducer = createReducer(initialState, (builder) => { // data queries actions @@ -70,6 +71,19 @@ export const queriesAndExpressionsReducer = createReducer(initialState, (builder .addCase(setDataQueries, (state, { payload }) => { const expressionQueries = state.queries.filter((query) => isExpressionQuery(query.model)); state.queries = [...payload, ...expressionQueries]; + }) + .addCase(updateMaxDataPoints, (state, action) => { + state.queries = state.queries.map((query) => { + return query.refId === action.payload.refId + ? { + ...query, + model: { + ...query.model, + maxDataPoints: action.payload.maxDataPoints, + }, + } + : query; + }); }); // expressions actions From e5e8bb4dea9cbf852b45852fd783e8c92f9cac89 Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:20:28 +0200 Subject: [PATCH 21/46] Chore: Use same JSON tag casing everywhere for parent UID (#61935) Chore: Use same JSON tag casing everywhere for parent UID --- pkg/api/dtos/folder.go | 2 +- pkg/api/folder.go | 4 +-- public/api-merged.json | 72 +++++++++++++++++++----------------------- public/api-spec.json | 9 ++---- public/openapi3.json | 72 +++++++++++++++++++----------------------- 5 files changed, 72 insertions(+), 87 deletions(-) diff --git a/pkg/api/dtos/folder.go b/pkg/api/dtos/folder.go index 54b96b603f8..618a64a0d45 100644 --- a/pkg/api/dtos/folder.go +++ b/pkg/api/dtos/folder.go @@ -31,5 +31,5 @@ type FolderSearchHit struct { Uid string `json:"uid"` Title string `json:"title"` AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"` - ParentUID string `json:"parent_uid,omitempty"` + ParentUID string `json:"parentUid,omitempty"` } diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 1a5f35f11bd..bfc6cad7e90 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -39,7 +39,7 @@ func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response { OrgID: c.OrgID, Limit: c.QueryInt64("limit"), Page: c.QueryInt64("page"), - UID: c.Query("parent_uid"), + UID: c.Query("parentUid"), SignedInUser: c.SignedInUser, }) } else { @@ -340,7 +340,7 @@ type GetFoldersParams struct { // The parent folder UID // in:query // required:false - ParentUID string `json:"parent_uid"` + ParentUID string `json:"parentUid"` } // swagger:parameters getFolderByUID diff --git a/public/api-merged.json b/public/api-merged.json index 10c3730b5c2..56fd778555b 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -3058,7 +3058,7 @@ "tags": [ "provisioning" ], - "summary": "Updates an existing template.", + "summary": "Updates an existing notification template.", "operationId": "RoutePutTemplate", "parameters": [ { @@ -5240,7 +5240,7 @@ }, "/folders": { "get": { - "description": "Returns all folders that the authenticated user has permission to view.\nIf nested folders are enabled, it expects an additional query parameter with the parent folder UID.", + "description": "Returns all folders that the authenticated user has permission to view.\nIf nested folders are enabled, it expects an additional query parameter with the parent folder UID\nand returns the immediate subfolders.", "tags": [ "folders" ], @@ -5266,7 +5266,7 @@ { "type": "string", "description": "The parent folder UID", - "name": "parent_uid", + "name": "parentUid", "in": "query" } ], @@ -12218,9 +12218,6 @@ "hasAcl": { "type": "boolean" }, - "hasPublicDashboard": { - "type": "boolean" - }, "isFolder": { "type": "boolean" }, @@ -13179,7 +13176,7 @@ "type": "integer", "format": "int64" }, - "parent_uid": { + "parentUid": { "type": "string" }, "title": { @@ -14447,34 +14444,6 @@ "$ref": "#/definitions/Matcher" } }, - "NotificationTemplate": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "provenance": { - "$ref": "#/definitions/Provenance" - }, - "template": { - "type": "string" - } - } - }, - "NotificationTemplateContent": { - "type": "object", - "properties": { - "template": { - "type": "string" - } - } - }, - "NotificationTemplates": { - "type": "array", - "items": { - "$ref": "#/definitions/NotificationTemplate" - } - }, "Metadata": { "description": "Metadata contains user accesses for a given resource\nEx: map[string]bool{\"create\":true, \"delete\": true}", "type": "object", @@ -14647,6 +14616,34 @@ "format": "int64", "title": "NoticeSeverity is a type for the Severity property of a Notice." }, + "NotificationTemplate": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "provenance": { + "$ref": "#/definitions/Provenance" + }, + "template": { + "type": "string" + } + } + }, + "NotificationTemplateContent": { + "type": "object", + "properties": { + "template": { + "type": "string" + } + } + }, + "NotificationTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/NotificationTemplate" + } + }, "NotificationTestCommand": { "type": "object", "properties": { @@ -18578,7 +18575,6 @@ } }, "alertGroups": { - "description": "AlertGroups alert groups", "type": "array", "items": { "$ref": "#/definitions/alertGroup" @@ -18739,14 +18735,12 @@ } }, "gettableAlerts": { - "description": "GettableAlerts gettable alerts", "type": "array", "items": { "$ref": "#/definitions/gettableAlert" } }, "gettableSilence": { - "description": "GettableSilence gettable silence", "type": "object", "required": [ "comment", @@ -18795,7 +18789,6 @@ } }, "gettableSilences": { - "description": "GettableSilences gettable silences", "type": "array", "items": { "$ref": "#/definitions/gettableSilence" @@ -18982,6 +18975,7 @@ } }, "receiver": { + "description": "Receiver receiver", "type": "object", "required": [ "active", diff --git a/public/api-spec.json b/public/api-spec.json index 14bca1499d2..b342294edea 100644 --- a/public/api-spec.json +++ b/public/api-spec.json @@ -4568,7 +4568,7 @@ }, "/folders": { "get": { - "description": "Returns all folders that the authenticated user has permission to view.\nIf nested folders are enabled, it expects an additional query parameter with the parent folder UID.", + "description": "Returns all folders that the authenticated user has permission to view.\nIf nested folders are enabled, it expects an additional query parameter with the parent folder UID\nand returns the immediate subfolders.", "tags": [ "folders" ], @@ -4594,7 +4594,7 @@ { "type": "string", "description": "The parent folder UID", - "name": "parent_uid", + "name": "parentUid", "in": "query" } ], @@ -11159,9 +11159,6 @@ "hasAcl": { "type": "boolean" }, - "hasPublicDashboard": { - "type": "boolean" - }, "isFolder": { "type": "boolean" }, @@ -11919,7 +11916,7 @@ "type": "integer", "format": "int64" }, - "parent_uid": { + "parentUid": { "type": "string" }, "title": { diff --git a/public/openapi3.json b/public/openapi3.json index f499bc9b891..866480c6390 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -3534,9 +3534,6 @@ "hasAcl": { "type": "boolean" }, - "hasPublicDashboard": { - "type": "boolean" - }, "isFolder": { "type": "boolean" }, @@ -4495,7 +4492,7 @@ "format": "int64", "type": "integer" }, - "parent_uid": { + "parentUid": { "type": "string" }, "title": { @@ -5764,34 +5761,6 @@ }, "type": "array" }, - "NotificationTemplate": { - "properties": { - "name": { - "type": "string" - }, - "provenance": { - "$ref": "#/components/schemas/Provenance" - }, - "template": { - "type": "string" - } - }, - "type": "object" - }, - "NotificationTemplateContent": { - "properties": { - "template": { - "type": "string" - } - }, - "type": "object" - }, - "NotificationTemplates": { - "items": { - "$ref": "#/components/schemas/NotificationTemplate" - }, - "type": "array" - }, "Metadata": { "additionalProperties": { "type": "boolean" @@ -5964,6 +5933,34 @@ "title": "NoticeSeverity is a type for the Severity property of a Notice.", "type": "integer" }, + "NotificationTemplate": { + "properties": { + "name": { + "type": "string" + }, + "provenance": { + "$ref": "#/components/schemas/Provenance" + }, + "template": { + "type": "string" + } + }, + "type": "object" + }, + "NotificationTemplateContent": { + "properties": { + "template": { + "type": "string" + } + }, + "type": "object" + }, + "NotificationTemplates": { + "items": { + "$ref": "#/components/schemas/NotificationTemplate" + }, + "type": "array" + }, "NotificationTestCommand": { "properties": { "disableResolveMessage": { @@ -9893,7 +9890,6 @@ "type": "object" }, "alertGroups": { - "description": "AlertGroups alert groups", "items": { "$ref": "#/components/schemas/alertGroup" }, @@ -10054,14 +10050,12 @@ "type": "object" }, "gettableAlerts": { - "description": "GettableAlerts gettable alerts", "items": { "$ref": "#/components/schemas/gettableAlert" }, "type": "array" }, "gettableSilence": { - "description": "GettableSilence gettable silence", "properties": { "comment": { "description": "comment", @@ -10110,7 +10104,6 @@ "type": "object" }, "gettableSilences": { - "description": "GettableSilences gettable silences", "items": { "$ref": "#/components/schemas/gettableSilence" }, @@ -10297,6 +10290,7 @@ "type": "object" }, "receiver": { + "description": "Receiver receiver", "properties": { "active": { "description": "active", @@ -13835,7 +13829,7 @@ "description": "ValidationError" } }, - "summary": "Updates an existing template.", + "summary": "Updates an existing notification template.", "tags": [ "provisioning" ] @@ -16131,7 +16125,7 @@ }, "/folders": { "get": { - "description": "Returns all folders that the authenticated user has permission to view.\nIf nested folders are enabled, it expects an additional query parameter with the parent folder UID.", + "description": "Returns all folders that the authenticated user has permission to view.\nIf nested folders are enabled, it expects an additional query parameter with the parent folder UID\nand returns the immediate subfolders.", "operationId": "getFolders", "parameters": [ { @@ -16157,7 +16151,7 @@ { "description": "The parent folder UID", "in": "query", - "name": "parent_uid", + "name": "parentUid", "schema": { "type": "string" } From 38d3d1c02b7d468722106bb55de15ed2db254320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 24 Jan 2023 09:32:49 +0100 Subject: [PATCH 22/46] Profile: Rename profile page from preferences to profile (#61777) * Rename preferences to Profile * Fixed tests and ran i18 extract --- pkg/services/navtree/navtreeimpl/navtree.go | 2 +- public/app/core/components/NavBar/navBarItem-translations.ts | 2 +- public/app/features/profile/UserProfileEditForm.tsx | 2 +- public/app/features/profile/UserProfileEditPage.test.tsx | 1 - public/locales/de-DE/grafana.json | 3 +-- public/locales/en-US/grafana.json | 5 ++--- public/locales/es-ES/grafana.json | 3 +-- public/locales/fr-FR/grafana.json | 3 +-- public/locales/pseudo-LOCALE/grafana.json | 5 ++--- public/locales/zh-Hans/grafana.json | 3 +-- 10 files changed, 11 insertions(+), 18 deletions(-) diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 5e7a3d024dd..2b4285679ad 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -279,7 +279,7 @@ func (s *ServiceImpl) getProfileNode(c *models.ReqContext) *navtree.NavLink { children := []*navtree.NavLink{ { - Text: "Preferences", Id: "profile/settings", Url: s.cfg.AppSubURL + "/profile", Icon: "sliders-v-alt", + Text: "Profile", Id: "profile/settings", Url: s.cfg.AppSubURL + "/profile", Icon: "sliders-v-alt", }, } diff --git a/public/app/core/components/NavBar/navBarItem-translations.ts b/public/app/core/components/NavBar/navBarItem-translations.ts index cf58d991c7c..f47058741eb 100644 --- a/public/app/core/components/NavBar/navBarItem-translations.ts +++ b/public/app/core/components/NavBar/navBarItem-translations.ts @@ -126,7 +126,7 @@ export function getNavTitle(navId: string | undefined) { case 'help': return t('nav.help.title', 'Help'); case 'profile/settings': - return t('nav.profile/settings.title', 'Preferences'); + return t('nav.profile/settings.title', 'Profile'); case 'profile/notifications': return t('nav.profile/notifications.title', 'Notification history'); case 'profile/password': diff --git a/public/app/features/profile/UserProfileEditForm.tsx b/public/app/features/profile/UserProfileEditForm.tsx index 530121c5c9d..1a2f76935c5 100644 --- a/public/app/features/profile/UserProfileEditForm.tsx +++ b/public/app/features/profile/UserProfileEditForm.tsx @@ -31,7 +31,7 @@ export const UserProfileEditForm: FC = ({ user, isSavingUser, updateProfi
{({ register, errors }) => { return ( -
Profile}> +
{ await getTestContext(); const { name, email, username, saveProfile } = getSelectors(); - expect(screen.getByText(/profile/i)).toBeInTheDocument(); expect(name()).toBeInTheDocument(); expect(name()).toHaveValue('Test User'); expect(email()).toBeInTheDocument(); diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 1ea23009464..dd79858a00e 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -555,8 +555,7 @@ "name-error": "Name ist erforderlich", "name-label": "Name", "username-label": "Benutzername" - }, - "title": "Profil" + } }, "user-session": { "browser-column": "Browser & Betriebssystem", diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 328381a74be..c2bb24742a0 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -285,7 +285,7 @@ "title": "Change password" }, "profile/settings": { - "title": "Preferences" + "title": "Profile" }, "profile/switch-org": "Switch organization", "scenes": { @@ -555,8 +555,7 @@ "name-error": "Name is required", "name-label": "Name", "username-label": "Username" - }, - "title": "Profile" + } }, "user-session": { "browser-column": "Browser & OS", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index 3f8c566d9ba..24d17509b06 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -555,8 +555,7 @@ "name-error": "El nombre es obligatorio", "name-label": "Nombre", "username-label": "Nombre de usuario" - }, - "title": "Perfil" + } }, "user-session": { "browser-column": "Navegador y sistema operativo", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index b01904456ba..76f0cf8e44c 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -555,8 +555,7 @@ "name-error": "Un nom est obligatoire", "name-label": "Nom", "username-label": "Nom d’utilisateur" - }, - "title": "Profil" + } }, "user-session": { "browser-column": "Navigateur et système d'exploitation", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 78c9ecf18bb..b480c15e316 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -285,7 +285,7 @@ "title": "Cĥäʼnģę päşşŵőřđ" }, "profile/settings": { - "title": "Přęƒęřęʼnčęş" + "title": "Přőƒįľę" }, "profile/switch-org": "Ŝŵįŧčĥ őřģäʼnįžäŧįőʼn", "scenes": { @@ -555,8 +555,7 @@ "name-error": "Ńämę įş řęqūįřęđ", "name-label": "Ńämę", "username-label": "Ůşęřʼnämę" - }, - "title": "Přőƒįľę" + } }, "user-session": { "browser-column": "ßřőŵşęř & ØŜ", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index de1caaa8f02..756b9b2c3b5 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -555,8 +555,7 @@ "name-error": "姓名是必填项", "name-label": "姓名", "username-label": "用户名" - }, - "title": "用户资料" + } }, "user-session": { "browser-column": "浏览器和操作系统", From 7c786c11e3f0c3a222409894f6da3732924b1c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 24 Jan 2023 09:33:12 +0100 Subject: [PATCH 23/46] Home: Fixes breadcrumb for custom home dashboard (#61499) * Home: Fixes breadcrumb for custom home dashboard * restore using home page cfg setting --- pkg/services/navtree/navtreeimpl/navtree.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 2b4285679ad..a5a1215e7f7 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -218,14 +218,6 @@ func (s *ServiceImpl) getHomeNode(c *models.ReqContext, prefs *pref.Preference) homeUrl = homePage } - if prefs.HomeDashboardID != 0 { - slugQuery := dashboards.GetDashboardRefByIDQuery{ID: prefs.HomeDashboardID} - err := s.dashboardService.GetDashboardUIDByID(c.Req.Context(), &slugQuery) - if err == nil { - homeUrl = dashboards.GetDashboardURL(slugQuery.Result.UID, slugQuery.Result.Slug) - } - } - homeNode := &navtree.NavLink{ Text: "Home", Id: "home", From 479da46a9e119f2a81a62f48e073554350dbe867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20L=C3=B3pez=20de=20la=20Franca=20Beltran?= <5459617+joanlopez@users.noreply.github.com> Date: Tue, 24 Jan 2023 09:37:32 +0100 Subject: [PATCH 24/46] Kindsys: Include @grafanamaturity counts to Kinds report (#61911) * Kindsys: Include @grafanamaturity counts to Kinds report * Replace reflect.DeepEqual with regular (in)equality op * Move reference check to its use (inline) * Fix linter complains --- pkg/kindsys/kindsysreport/kindsysreport.go | 74 ++++++++++++++++++++++ pkg/kindsys/report.go | 31 ++++++--- pkg/kindsys/report.json | 74 ++++++++++++++++++++++ 3 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 pkg/kindsys/kindsysreport/kindsysreport.go diff --git a/pkg/kindsys/kindsysreport/kindsysreport.go b/pkg/kindsys/kindsysreport/kindsysreport.go new file mode 100644 index 00000000000..c51a4b3df74 --- /dev/null +++ b/pkg/kindsys/kindsysreport/kindsysreport.go @@ -0,0 +1,74 @@ +package kindsysreport + +import ( + "cuelang.org/go/cue" +) + +type AttributeWalker struct { + seen map[cue.Value]bool + count map[string]int +} + +func (w *AttributeWalker) Count(sch cue.Value, attrs ...string) map[string]int { + w.seen = make(map[cue.Value]bool) + w.count = make(map[string]int) + + for _, attr := range attrs { + w.count[attr] = 0 + } + + w.walk(cue.MakePath(), sch) + return w.count +} + +func (w *AttributeWalker) walk(p cue.Path, v cue.Value) { + if w.seen[v] { + return + } + + w.seen[v] = true + + for attr := range w.count { + if found := v.Attribute(attr); found.Err() == nil { + w.count[attr]++ + } + } + + // nolint: exhaustive + switch v.Kind() { + case cue.StructKind: + // If current cue.Value is a reference to another + // definition, we don't want to traverse its fields + // individually, because we'll do so for the actual def. + if v != cue.Dereference(v) { + return + } + + iter, err := v.Fields(cue.All()) + if err != nil { + panic(err) + } + + for iter.Next() { + w.walk(appendPath(p, iter.Selector()), iter.Value()) + } + if lv := v.LookupPath(cue.MakePath(cue.AnyString)); lv.Exists() { + w.walk(appendPath(p, cue.AnyString), lv) + } + case cue.ListKind: + list, err := v.List() + if err != nil { + panic(err) + } + for i := 0; list.Next(); i++ { + w.walk(appendPath(p, cue.Index(i)), list.Value()) + } + if lv := v.LookupPath(cue.MakePath(cue.AnyIndex)); lv.Exists() { + w.walk(appendPath(p, cue.AnyString), lv) + } + } +} + +func appendPath(p cue.Path, sel cue.Selector) cue.Path { + return cue.MakePath(append(p.Selectors(), sel)...) +} diff --git a/pkg/kindsys/report.go b/pkg/kindsys/report.go index cd565cfa0d2..70a955d4604 100644 --- a/pkg/kindsys/report.go +++ b/pkg/kindsys/report.go @@ -15,8 +15,11 @@ import ( "sort" "strings" + "cuelang.org/go/cue" + "github.com/grafana/codejen" "github.com/grafana/grafana/pkg/kindsys" + "github.com/grafana/grafana/pkg/kindsys/kindsysreport" "github.com/grafana/grafana/pkg/plugins/pfs/corelist" "github.com/grafana/grafana/pkg/plugins/plugindef" "github.com/grafana/grafana/pkg/registry/corekind" @@ -82,8 +85,9 @@ type KindLinks struct { type Kind struct { kindsys.SomeKindProperties - Category string - Links KindLinks + Category string + Links KindLinks + GrafanaMaturityCount int } // MarshalJSON is overwritten to marshal @@ -100,6 +104,7 @@ func (k Kind) MarshalJSON() ([]byte, error) { } m["category"] = k.Category + m["grafanaMaturityCount"] = k.GrafanaMaturityCount m["links"] = map[string]string{} for _, ref := range []string{"Schema", "Go", "Ts", "Docs"} { @@ -177,13 +182,14 @@ func buildKindStateReport() *KindStateReport { seen := make(map[string]bool) for _, k := range b.All() { seen[k.Props().Common().Name] = true - k.Lineage() + lin := k.Lineage() switch k.Props().(type) { case kindsys.CoreProperties: r.add(Kind{ - SomeKindProperties: k.Props(), - Category: "core", - Links: buildCoreLinks(k.Lineage(), k.Decl().Properties), + SomeKindProperties: k.Props(), + Category: "core", + Links: buildCoreLinks(lin, k.Decl().Properties), + GrafanaMaturityCount: grafanaMaturityAttrCount(lin.Latest().Underlying()), }) } } @@ -212,9 +218,10 @@ func buildKindStateReport() *KindStateReport { for _, si := range all { if ck, has := pp.ComposableKinds[si.Name()]; has { r.add(Kind{ - SomeKindProperties: ck.Props(), - Category: "composable", - Links: buildComposableLinks(pp.Properties, ck.Decl().Properties), + SomeKindProperties: ck.Props(), + Category: "composable", + Links: buildComposableLinks(pp.Properties, ck.Decl().Properties), + GrafanaMaturityCount: grafanaMaturityAttrCount(ck.Lineage().Latest().Underlying()), }) } else if may := si.Should(string(pp.Properties.Type)); may { n := plugindef.DerivePascalName(pp.Properties) + si.Name() @@ -306,6 +313,12 @@ func buildComposableLinks(pp plugindef.PluginDef, cp kindsys.ComposablePropertie } } +func grafanaMaturityAttrCount(sch cue.Value) int { + const attr = "grafanamaturity" + aw := new(kindsysreport.AttributeWalker) + return aw.Count(sch, attr)[attr] +} + func machinize(s string) string { return strings.Map(func(r rune) rune { switch { diff --git a/pkg/kindsys/report.json b/pkg/kindsys/report.json index b7d2378d7dd..5878ac71d90 100644 --- a/pkg/kindsys/report.json +++ b/pkg/kindsys/report.json @@ -6,6 +6,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -26,6 +27,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -46,6 +48,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -66,6 +69,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -86,6 +90,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/annotationslistpanelcfg/schema-reference", @@ -106,6 +111,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -125,6 +131,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -145,6 +152,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -165,6 +173,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/barchartpanelcfg/schema-reference", @@ -185,6 +194,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/bargaugepanelcfg/schema-reference", @@ -205,6 +215,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -225,6 +236,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -245,6 +257,7 @@ 0, 0 ], + "grafanaMaturityCount": 144, "lineageIsGroup": false, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/core/dashboard/schema-reference", @@ -264,6 +277,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -284,6 +298,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -304,6 +319,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/dashboardlistpanelcfg/schema-reference", @@ -324,6 +340,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -343,6 +360,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -363,6 +381,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -383,6 +402,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -403,6 +423,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -423,6 +444,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -442,6 +464,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/gaugepanelcfg/schema-reference", @@ -462,6 +485,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -482,6 +506,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -502,6 +527,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -522,6 +548,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -542,6 +569,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -562,6 +590,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -582,6 +611,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -602,6 +632,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -622,6 +653,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -642,6 +674,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/histogrampanelcfg/schema-reference", @@ -662,6 +695,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -682,6 +716,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -702,6 +737,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -722,6 +758,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -742,6 +779,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -762,6 +800,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -782,6 +821,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -802,6 +842,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -822,6 +863,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -842,6 +884,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -862,6 +905,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -882,6 +926,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/newspanelcfg/schema-reference", @@ -902,6 +947,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -922,6 +968,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -942,6 +989,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -962,6 +1010,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -982,6 +1031,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1002,6 +1052,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/piechartpanelcfg/schema-reference", @@ -1022,6 +1073,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/core/playlist/schema-reference", @@ -1041,6 +1093,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1061,6 +1114,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1081,6 +1135,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1101,6 +1156,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1121,6 +1177,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1140,6 +1197,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1159,6 +1217,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1178,6 +1237,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/statpanelcfg/schema-reference", @@ -1198,6 +1258,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1218,6 +1279,7 @@ 0, 0 ], + "grafanaMaturityCount": 7, "lineageIsGroup": false, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/core/team/schema-reference", @@ -1237,6 +1299,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1257,6 +1320,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1277,6 +1341,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1297,6 +1362,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1317,6 +1383,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/textpanelcfg/schema-reference", @@ -1337,6 +1404,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1356,6 +1424,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1376,6 +1445,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1395,6 +1465,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1415,6 +1486,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", @@ -1435,6 +1507,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": false, "links": { "docs": "n/a", @@ -1455,6 +1528,7 @@ 0, 0 ], + "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { "docs": "n/a", From 92a750a732cbfed46f325aae0391ae34bf623bfc Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Tue, 24 Jan 2023 09:20:47 +0000 Subject: [PATCH 25/46] Chore: convert last test to RTL and remove Enzyme references (#61918) convert last test to RTL and remove enzyme references --- .betterer.results | 14 - .betterer.ts | 3 - jest.config.js | 1 - package.json | 5 - packages/grafana-toolkit/README.md | 9 +- packages/grafana-ui/package.json | 4 - .../components/QueryField/QueryField.test.tsx | 177 +++++----- packages/jaeger-ui-components/package.json | 2 - public/test/jest-setup.ts | 4 - yarn.lock | 331 +----------------- 10 files changed, 98 insertions(+), 452 deletions(-) diff --git a/.betterer.results b/.betterer.results index c52010d3415..fcb25c5f876 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3,14 +3,6 @@ // If this file contains merge conflicts, use `betterer merge` to automatically resolve them: // https://phenomnomnominal.github.io/betterer/docs/results-file/#merge // -exports[`no enzyme tests`] = { - value: `{ - "packages/grafana-ui/src/components/QueryField/QueryField.test.tsx:2976628669": [ - [0, 26, 13, "RegExp match", "2409514259"] - ] - }` -}; - exports[`better eslint`] = { value: `{ "e2e/benchmarks/live/4-20hz-panels.spec.ts:5381": [ @@ -1315,12 +1307,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "packages/grafana-ui/src/components/QueryField/QueryField.test.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] - ], "packages/grafana-ui/src/components/QueryField/QueryField.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"] diff --git a/.betterer.ts b/.betterer.ts index af60ac38937..cf7e809107f 100644 --- a/.betterer.ts +++ b/.betterer.ts @@ -1,13 +1,10 @@ -import { regexp } from '@betterer/regexp'; import { BettererFileTest } from '@betterer/betterer'; import { ESLint, Linter } from 'eslint'; import { existsSync } from 'fs'; -import { exec } from 'child_process'; import path from 'path'; import glob from 'glob'; export default { - 'no enzyme tests': () => regexp(/from 'enzyme'/g).include('**/*.test.*'), 'better eslint': () => countEslintErrors().include('**/*.{ts,tsx}'), 'no undocumented stories': () => countUndocumentedStories().include('**/*.story.tsx'), }; diff --git a/jest.config.js b/jest.config.js index f71574e2db9..7db7afdf68c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -20,7 +20,6 @@ module.exports = { testTimeout: 30000, resolver: `/public/test/jest-resolver.js`, setupFilesAfterEnv: ['./public/test/setupTests.ts'], - snapshotSerializers: ['enzyme-to-json/serializer'], globals: { __webpack_public_path__: '', // empty string }, diff --git a/package.json b/package.json index 52b4ff06286..23a3f9bcd2b 100644 --- a/package.json +++ b/package.json @@ -123,8 +123,6 @@ "@types/d3-force": "^2.1.0", "@types/d3-scale-chromatic": "1.3.1", "@types/debounce-promise": "3.1.5", - "@types/enzyme": "3.10.12", - "@types/enzyme-adapter-react-16": "1.0.6", "@types/eslint": "8.4.9", "@types/file-saver": "2.0.5", "@types/glob": "^8.0.0", @@ -167,7 +165,6 @@ "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.42.0", "@typescript-eslint/parser": "5.42.0", - "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", "autoprefixer": "10.4.13", "babel-jest": "29.3.1", "babel-loader": "9.1.0", @@ -179,8 +176,6 @@ "css-loader": "6.7.1", "css-minimizer-webpack-plugin": "4.2.2", "cypress": "9.5.1", - "enzyme": "3.11.0", - "enzyme-to-json": "3.6.2", "esbuild": "0.16.17", "esbuild-loader": "2.21.0", "esbuild-plugin-browserslist": "^0.6.0", diff --git a/packages/grafana-toolkit/README.md b/packages/grafana-toolkit/README.md index 8b853ac6b41..800492fa133 100644 --- a/packages/grafana-toolkit/README.md +++ b/packages/grafana-toolkit/README.md @@ -146,14 +146,7 @@ Yes! grafana-toolkit supports TypeScript by default. grafana-toolkit comes with Jest as a test runner. -Internally at Grafana we use Enzyme. If you are developing React plugin and you want to configure Enzyme as a testing utility, then you need to configure `enzyme-adapter-react`. To do so, create `/config/jest-setup.ts` file that will provide necessary setup. Copy the following code into that file to get Enzyme working with React: - -```ts -import { configure } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; - -configure({ adapter: new Adapter() }); -``` +Internally at Grafana we use React Testing Library. You can also set up Jest with shims of your needs by creating `jest-shim.ts` file in the same directory: `/config/jest-shim.ts` diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 20678316f77..dc219b691c4 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -141,8 +141,6 @@ "@testing-library/user-event": "14.4.3", "@types/common-tags": "^1.8.0", "@types/d3": "7.4.0", - "@types/enzyme": "3.10.12", - "@types/enzyme-adapter-react-16": "1.0.6", "@types/hoist-non-react-statics": "3.3.1", "@types/is-hotkey": "0.1.7", "@types/jest": "29.2.3", @@ -168,11 +166,9 @@ "@types/testing-library__jest-dom": "5.14.5", "@types/tinycolor2": "1.4.3", "@types/uuid": "8.3.4", - "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", "common-tags": "1.8.2", "css-loader": "6.7.1", "csstype": "3.1.1", - "enzyme": "3.11.0", "esbuild": "0.16.17", "expose-loader": "4.0.0", "mock-raf": "1.0.1", diff --git a/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx b/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx index b661ed4b312..7557fa5d7b3 100644 --- a/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx +++ b/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx @@ -1,6 +1,5 @@ -import { mount, shallow } from 'enzyme'; +import { render } from '@testing-library/react'; import React from 'react'; -import { Editor } from 'slate-react'; import { createTheme } from '@grafana/data'; @@ -8,120 +7,102 @@ import { UnThemedQueryField } from './QueryField'; describe('', () => { it('should render with null initial value', () => { - const wrapper = shallow( - - ); - expect(wrapper.find('div').exists()).toBeTruthy(); + expect(() => + render( + + ) + ).not.toThrow(); }); it('should render with empty initial value', () => { - const wrapper = shallow( - - ); - expect(wrapper.find('div').exists()).toBeTruthy(); + expect(() => + render() + ).not.toThrow(); }); it('should render with initial value', () => { - const wrapper = shallow( - - ); - expect(wrapper.find('div').exists()).toBeTruthy(); + expect(() => + render( + + ) + ).not.toThrow(); }); - it('should execute query on blur', () => { - const onRun = jest.fn(); - const wrapper = mount( - - ); - const field = wrapper.instance() as UnThemedQueryField; - const ed = wrapper.find(Editor).instance() as Editor; - expect(onRun.mock.calls.length).toBe(0); - field.handleBlur(undefined, ed, () => {}); - expect(onRun.mock.calls.length).toBe(1); - }); - - it('should run onChange with clean text', () => { - const onChange = jest.fn(); - const wrapper = shallow( - - ); - const field = wrapper.instance() as UnThemedQueryField; - field.runOnChange(); - expect(onChange.mock.calls.length).toBe(1); - expect(onChange.mock.calls[0][0]).toBe('my clean query '); - }); - - it('should run custom on blur, but not necessarily execute query', () => { - const onBlur = jest.fn(); - const onRun = jest.fn(); - const wrapper = mount( - - ); - const field = wrapper.instance() as UnThemedQueryField; - const ed = wrapper.find(Editor).instance() as Editor; - expect(onBlur.mock.calls.length).toBe(0); - expect(onRun.mock.calls.length).toBe(0); - field.handleBlur(undefined, ed, () => {}); - expect(onBlur.mock.calls.length).toBe(1); - expect(onRun.mock.calls.length).toBe(0); - }); describe('syntaxLoaded', () => { it('should re-render the editor after syntax has fully loaded', () => { - const wrapper: any = shallow( - + const mockOnRichValueChange = jest.fn(); + const { rerender } = render( + ); - const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn()); - wrapper.instance().editor = { insertText: () => ({ deleteBackward: () => ({ value: 'fooo' }) }) }; - wrapper.setProps({ syntaxLoaded: true }); - expect(spyOnChange).toHaveBeenCalledWith('fooo', true); + rerender( + + ); + expect(mockOnRichValueChange).toHaveBeenCalled(); }); + it('should not re-render the editor if syntax is already loaded', () => { - const wrapper: any = shallow( - + const mockOnRichValueChange = jest.fn(); + const { rerender } = render( + ); - const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn()); - wrapper.setProps({ syntaxLoaded: true }); - wrapper.instance().editor = {}; - wrapper.setProps({ syntaxLoaded: true }); - expect(spyOnChange).not.toBeCalled(); - }); - it('should not re-render the editor if editor itself is not defined', () => { - const wrapper: any = shallow( - + rerender( + ); - const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn()); - wrapper.setProps({ syntaxLoaded: true }); - expect(wrapper.instance().editor).toBeFalsy(); - expect(spyOnChange).not.toBeCalled(); + expect(mockOnRichValueChange).not.toBeCalled(); }); + it('should not re-render the editor twice once syntax is fully loaded', () => { - const wrapper: any = shallow( - + const mockOnRichValueChange = jest.fn(); + const { rerender } = render( + ); - const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn()); - wrapper.instance().editor = { insertText: () => ({ deleteBackward: () => ({ value: 'fooo' }) }) }; - wrapper.setProps({ syntaxLoaded: true }); - wrapper.setProps({ syntaxLoaded: true }); - expect(spyOnChange).toBeCalledTimes(1); + rerender( + + ); + rerender( + + ); + expect(mockOnRichValueChange).toBeCalledTimes(1); }); }); }); diff --git a/packages/jaeger-ui-components/package.json b/packages/jaeger-ui-components/package.json index cce91d58fb6..8984f23da5d 100644 --- a/packages/jaeger-ui-components/package.json +++ b/packages/jaeger-ui-components/package.json @@ -14,7 +14,6 @@ "@testing-library/react": "12.1.4", "@testing-library/user-event": "14.4.3", "@types/deep-freeze": "^0.1.1", - "@types/enzyme": "3.10.12", "@types/hoist-non-react-statics": "^3.3.1", "@types/jest": "29.2.3", "@types/lodash": "4.14.187", @@ -25,7 +24,6 @@ "@types/slate-react": "0.22.9", "@types/testing-library__jest-dom": "5.14.5", "@types/tinycolor2": "1.4.3", - "enzyme": "3.11.0", "sinon": "14.0.1", "typescript": "4.8.4" }, diff --git a/public/test/jest-setup.ts b/public/test/jest-setup.ts index cf9b5f735ab..1477e86bd88 100644 --- a/public/test/jest-setup.ts +++ b/public/test/jest-setup.ts @@ -2,9 +2,7 @@ // angular is imported. import './global-jquery-shim'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; import angular from 'angular'; -import { configure } from 'enzyme'; import { EventBusSrv } from '@grafana/data'; import { GrafanaBootConfig } from '@grafana/runtime'; @@ -67,8 +65,6 @@ jest.mock('../app/core/core', () => ({ jest.mock('../app/angular/partials', () => ({})); jest.mock('../app/features/plugins/plugin_loader', () => ({})); -configure({ adapter: new Adapter() }); - const localStorageMock = (() => { let store: any = {}; return { diff --git a/yarn.lock b/yarn.lock index a6adfe69b11..95d070c1c31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5081,8 +5081,6 @@ __metadata: "@testing-library/user-event": 14.4.3 "@types/common-tags": ^1.8.0 "@types/d3": 7.4.0 - "@types/enzyme": 3.10.12 - "@types/enzyme-adapter-react-16": 1.0.6 "@types/hoist-non-react-statics": 3.3.1 "@types/is-hotkey": 0.1.7 "@types/jest": 29.2.3 @@ -5108,7 +5106,6 @@ __metadata: "@types/testing-library__jest-dom": 5.14.5 "@types/tinycolor2": 1.4.3 "@types/uuid": 8.3.4 - "@wojtekmaj/enzyme-adapter-react-17": 0.8.0 ansicolor: 1.1.100 calculate-size: 1.1.1 classnames: 2.3.2 @@ -5118,7 +5115,6 @@ __metadata: csstype: 3.1.1 d3: 5.15.0 date-fns: 2.29.3 - enzyme: 3.11.0 esbuild: 0.16.17 expose-loader: 4.0.0 hoist-non-react-statics: 3.3.2 @@ -5323,7 +5319,6 @@ __metadata: "@testing-library/react": 12.1.4 "@testing-library/user-event": 14.4.3 "@types/deep-freeze": ^0.1.1 - "@types/enzyme": 3.10.12 "@types/hoist-non-react-statics": ^3.3.1 "@types/jest": 29.2.3 "@types/lodash": 4.14.187 @@ -5339,7 +5334,6 @@ __metadata: combokeys: ^3.0.0 copy-to-clipboard: ^3.1.0 deep-freeze: ^0.0.1 - enzyme: 3.11.0 fuzzy: ^0.1.3 hoist-non-react-statics: ^3.3.2 json-markup: ^1.1.0 @@ -10399,15 +10393,6 @@ __metadata: languageName: node linkType: hard -"@types/cheerio@npm:*, @types/cheerio@npm:^0.22.22": - version: 0.22.30 - resolution: "@types/cheerio@npm:0.22.30" - dependencies: - "@types/node": "*" - checksum: 2aba93f57c0c88964bd83c3403b1f9ad98c377d00e0d638417a943ab483f0a638925c9a4f2e25d923db2a293ffb59f833cd49fa76c6299684494633becea54de - languageName: node - linkType: hard - "@types/chrome-remote-interface@npm:0.31.4": version: 0.31.4 resolution: "@types/chrome-remote-interface@npm:0.31.4" @@ -10824,35 +10809,6 @@ __metadata: languageName: node linkType: hard -"@types/enzyme-adapter-react-16@npm:1.0.6": - version: 1.0.6 - resolution: "@types/enzyme-adapter-react-16@npm:1.0.6" - dependencies: - "@types/enzyme": "*" - checksum: d668ed5fbb7bf72e647f212ab60e2208f96b566a1782cbaa35cd0be3bfc27c5d075367517d341155d35dd21834271df7d74bbf49d1f878e0b7be2a9c0daa17a3 - languageName: node - linkType: hard - -"@types/enzyme@npm:*": - version: 3.10.10 - resolution: "@types/enzyme@npm:3.10.10" - dependencies: - "@types/cheerio": "*" - "@types/react": "*" - checksum: e2393f87d6737d643789fb1a83c53c5cb6cb9eaebf1e1c8a3163d95f778f3741a9734fea47761a7648d9c778166ffd531f61c0aa4c5bf97b0d8018cacad05a49 - languageName: node - linkType: hard - -"@types/enzyme@npm:3.10.12": - version: 3.10.12 - resolution: "@types/enzyme@npm:3.10.12" - dependencies: - "@types/cheerio": "*" - "@types/react": "*" - checksum: 356e9142566b68c9b324ae71a7b93f03512a1c009a1d337a25ce4f495590f3e79de08aa4a0016d6224cb228c27832d92b6d7d3276ba5962302c41d0577e8a912 - languageName: node - linkType: hard - "@types/eslint-scope@npm:^3.7.3": version: 3.7.4 resolution: "@types/eslint-scope@npm:3.7.4" @@ -12974,38 +12930,6 @@ __metadata: languageName: node linkType: hard -"@wojtekmaj/enzyme-adapter-react-17@npm:0.8.0": - version: 0.8.0 - resolution: "@wojtekmaj/enzyme-adapter-react-17@npm:0.8.0" - dependencies: - "@wojtekmaj/enzyme-adapter-utils": ^0.2.0 - enzyme-shallow-equal: ^1.0.0 - has: ^1.0.0 - prop-types: ^15.7.0 - react-is: ^17.0.0 - react-test-renderer: ^17.0.0 - peerDependencies: - enzyme: ^3.0.0 - react: ^17.0.0-0 - react-dom: ^17.0.0-0 - checksum: aa9674f06f6db269b72168ebf46c4513938993479eb60bac30cb6183b5aca6108ade3d08af4f56c142cb219415480d0c4b454ba9452b85c32f711c806b39cd8c - languageName: node - linkType: hard - -"@wojtekmaj/enzyme-adapter-utils@npm:^0.2.0": - version: 0.2.0 - resolution: "@wojtekmaj/enzyme-adapter-utils@npm:0.2.0" - dependencies: - function.prototype.name: ^1.1.0 - has: ^1.0.0 - object.fromentries: ^2.0.0 - prop-types: ^15.7.0 - peerDependencies: - react: ^17.0.0-0 - checksum: 837741f1382acdb02ce304745eccfdcff03f1cae2a4fb833056a7a753308cd1182b0b32a10a04be6bfedaaab8f4acd5b458bfe0b9ebaa6119c4aaaba74a14ae4 - languageName: node - linkType: hard - "@xmldom/xmldom@npm:^0.8.3": version: 0.8.6 resolution: "@xmldom/xmldom@npm:0.8.6" @@ -13812,19 +13736,6 @@ __metadata: languageName: node linkType: hard -"array.prototype.filter@npm:^1.0.0": - version: 1.0.1 - resolution: "array.prototype.filter@npm:1.0.1" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.0 - es-array-method-boxes-properly: ^1.0.0 - is-string: ^1.0.7 - checksum: 574b52dcebf2def7bedb05449b60e5e3819093fa77f88c3f87a9611361d2745c7aacde01cd3ed7accafd632ee1e0340b655dd26dc7c060429cb4566058e63134 - languageName: node - linkType: hard - "array.prototype.flat@npm:^1.2.1": version: 1.3.1 resolution: "array.prototype.flat@npm:1.3.1" @@ -13837,17 +13748,6 @@ __metadata: languageName: node linkType: hard -"array.prototype.flat@npm:^1.2.3": - version: 1.2.5 - resolution: "array.prototype.flat@npm:1.2.5" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.0 - checksum: 9cc6414b111abfc7717e39546e4887b1e5ec74df8f1618d83425deaa95752bf05d475d1d241253b4d88d4a01f8e1bc84845ad5b7cc2047f8db2f614512acd40e - languageName: node - linkType: hard - "array.prototype.flat@npm:^1.2.5": version: 1.3.0 resolution: "array.prototype.flat@npm:1.3.0" @@ -15613,19 +15513,6 @@ __metadata: languageName: node linkType: hard -"cheerio-select@npm:^1.5.0": - version: 1.5.0 - resolution: "cheerio-select@npm:1.5.0" - dependencies: - css-select: ^4.1.3 - css-what: ^5.0.1 - domelementtype: ^2.2.0 - domhandler: ^4.2.0 - domutils: ^2.7.0 - checksum: d4506d8b9ad330a18f9de3a5a22138d0804063e92aac2fc020384cc52ab86d2194d2ae614fc87f0e2a62b6a6dd0c28ad23669cec64331172a9f99ad604863010 - languageName: node - linkType: hard - "cheerio-select@npm:^2.1.0": version: 2.1.0 resolution: "cheerio-select@npm:2.1.0" @@ -15655,21 +15542,6 @@ __metadata: languageName: node linkType: hard -"cheerio@npm:^1.0.0-rc.3": - version: 1.0.0-rc.10 - resolution: "cheerio@npm:1.0.0-rc.10" - dependencies: - cheerio-select: ^1.5.0 - dom-serializer: ^1.3.2 - domhandler: ^4.2.0 - htmlparser2: ^6.1.0 - parse5: ^6.0.1 - parse5-htmlparser2-tree-adapter: ^6.0.1 - tslib: ^2.2.0 - checksum: ace2f9c5809737534b1320d11d48762013694fa905b4deacac81a634edac178c1b0534f79d7b1896a88ce489db6cb539f222317996b21c8b6923ce413dcc1a2f - languageName: node - linkType: hard - "chokidar@npm:3.5.3, chokidar@npm:^3.3.1, chokidar@npm:^3.5.1, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" @@ -17011,7 +16883,7 @@ __metadata: languageName: node linkType: hard -"css-what@npm:^5.0.0, css-what@npm:^5.0.1": +"css-what@npm:^5.0.0": version: 5.1.0 resolution: "css-what@npm:5.1.0" checksum: 0b75d1bac95c885c168573c85744a6c6843d8c33345f54f717218b37ea6296b0e99bb12105930ea170fd4a921990392a7c790c16c585c1d8960c49e2b7ec39f7 @@ -18283,13 +18155,6 @@ __metadata: languageName: node linkType: hard -"discontinuous-range@npm:1.0.0": - version: 1.0.0 - resolution: "discontinuous-range@npm:1.0.0" - checksum: 8ee88d7082445b6eadc7c03bebe6dc978f96760c45e9f65d16ca66174d9e086a9e3855ee16acf65625e1a07a846a17de674f02a5964a6aebe5963662baf8b5c8 - languageName: node - linkType: hard - "djb2a@npm:^1.2.0": version: 1.2.0 resolution: "djb2a@npm:1.2.0" @@ -18382,7 +18247,7 @@ __metadata: languageName: node linkType: hard -"dom-serializer@npm:^1.0.1, dom-serializer@npm:^1.3.2": +"dom-serializer@npm:^1.0.1": version: 1.3.2 resolution: "dom-serializer@npm:1.3.2" dependencies: @@ -18468,7 +18333,7 @@ __metadata: languageName: node linkType: hard -"domutils@npm:^2.5.2, domutils@npm:^2.6.0, domutils@npm:^2.7.0": +"domutils@npm:^2.5.2, domutils@npm:^2.6.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" dependencies: @@ -18832,59 +18697,6 @@ __metadata: languageName: node linkType: hard -"enzyme-shallow-equal@npm:^1.0.0, enzyme-shallow-equal@npm:^1.0.1": - version: 1.0.4 - resolution: "enzyme-shallow-equal@npm:1.0.4" - dependencies: - has: ^1.0.3 - object-is: ^1.1.2 - checksum: 54bbad0955683f09252568bfcb9d7e934a27c06634057db9e82b54c0d9f7a27b6160d77643177d973c133b87d404f284cc6aa0481c0a1c81cdff05b072e2bb49 - languageName: node - linkType: hard - -"enzyme-to-json@npm:3.6.2": - version: 3.6.2 - resolution: "enzyme-to-json@npm:3.6.2" - dependencies: - "@types/cheerio": ^0.22.22 - lodash: ^4.17.21 - react-is: ^16.12.0 - peerDependencies: - enzyme: ^3.4.0 - checksum: e81f3dc05b5c440da416544a3cbc41fb9e79de0777453e48fe55de822f7d6f56ee08e5173d46a7624cf2781198396509c470bdd616a1ea441e6fa9ddf4396477 - languageName: node - linkType: hard - -"enzyme@npm:3.11.0": - version: 3.11.0 - resolution: "enzyme@npm:3.11.0" - dependencies: - array.prototype.flat: ^1.2.3 - cheerio: ^1.0.0-rc.3 - enzyme-shallow-equal: ^1.0.1 - function.prototype.name: ^1.1.2 - has: ^1.0.3 - html-element-map: ^1.2.0 - is-boolean-object: ^1.0.1 - is-callable: ^1.1.5 - is-number-object: ^1.0.4 - is-regex: ^1.0.5 - is-string: ^1.0.5 - is-subset: ^0.1.1 - lodash.escape: ^4.0.1 - lodash.isequal: ^4.5.0 - object-inspect: ^1.7.0 - object-is: ^1.0.2 - object.assign: ^4.1.0 - object.entries: ^1.1.1 - object.values: ^1.1.1 - raf: ^3.4.1 - rst-selector-parser: ^2.2.3 - string.prototype.trim: ^1.2.1 - checksum: 69ae80049c3f405122b8e619f1cf8b04f32b3cc2b6134c29ed8c0f05e87a0b15080f1121096ec211954a710f4787300af9157078c863012de87eee16e98e64ea - languageName: node - linkType: hard - "eol@npm:^0.9.1": version: 0.9.1 resolution: "eol@npm:0.9.1" @@ -21145,7 +20957,7 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.0, function.prototype.name@npm:^1.1.2, function.prototype.name@npm:^1.1.5": +"function.prototype.name@npm:^1.1.0, function.prototype.name@npm:^1.1.5": version: 1.1.5 resolution: "function.prototype.name@npm:1.1.5" dependencies: @@ -21853,8 +21665,6 @@ __metadata: "@types/d3-force": ^2.1.0 "@types/d3-scale-chromatic": 1.3.1 "@types/debounce-promise": 3.1.5 - "@types/enzyme": 3.10.12 - "@types/enzyme-adapter-react-16": 1.0.6 "@types/eslint": 8.4.9 "@types/file-saver": 2.0.5 "@types/glob": ^8.0.0 @@ -21906,7 +21716,6 @@ __metadata: "@visx/shape": 2.12.2 "@visx/tooltip": 2.16.0 "@welldone-software/why-did-you-render": 7.0.1 - "@wojtekmaj/enzyme-adapter-react-17": 0.8.0 angular: 1.8.3 angular-bindonce: 0.3.1 angular-route: 1.8.3 @@ -21939,8 +21748,6 @@ __metadata: date-fns: 2.29.3 debounce-promise: 3.1.2 emotion: 11.0.0 - enzyme: 3.11.0 - enzyme-to-json: 3.6.2 esbuild: 0.16.17 esbuild-loader: 2.21.0 esbuild-plugin-browserslist: ^0.6.0 @@ -22316,7 +22123,7 @@ __metadata: languageName: node linkType: hard -"has@npm:^1.0.0, has@npm:^1.0.3": +"has@npm:^1.0.3": version: 1.0.3 resolution: "has@npm:1.0.3" dependencies: @@ -22562,16 +22369,6 @@ __metadata: languageName: node linkType: hard -"html-element-map@npm:^1.2.0": - version: 1.3.1 - resolution: "html-element-map@npm:1.3.1" - dependencies: - array.prototype.filter: ^1.0.0 - call-bind: ^1.0.2 - checksum: 7408da008d37bfa76b597e298ae0ed530258065deb29fbd73d40f7cbd123b654d1022a7a8cfbe713e57d90c5bef844399f5c8a46cde7d55c91d305024c921d08 - languageName: node - linkType: hard - "html-encoding-sniffer@npm:^2.0.1": version: 2.0.1 resolution: "html-encoding-sniffer@npm:2.0.1" @@ -23532,7 +23329,7 @@ __metadata: languageName: node linkType: hard -"is-boolean-object@npm:^1.0.1, is-boolean-object@npm:^1.1.0": +"is-boolean-object@npm:^1.1.0": version: 1.1.2 resolution: "is-boolean-object@npm:1.1.2" dependencies: @@ -23572,7 +23369,7 @@ __metadata: languageName: node linkType: hard -"is-callable@npm:^1.1.4, is-callable@npm:^1.1.5, is-callable@npm:^1.2.4": +"is-callable@npm:^1.1.4, is-callable@npm:^1.2.4": version: 1.2.4 resolution: "is-callable@npm:1.2.4" checksum: 1a28d57dc435797dae04b173b65d6d1e77d4f16276e9eff973f994eadcfdc30a017e6a597f092752a083c1103cceb56c91e3dadc6692fedb9898dfaba701575f @@ -24030,7 +23827,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.0.5, is-regex@npm:^1.1.2, is-regex@npm:^1.1.4": +"is-regex@npm:^1.1.2, is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" dependencies: @@ -24118,13 +23915,6 @@ __metadata: languageName: node linkType: hard -"is-subset@npm:^0.1.1": - version: 0.1.1 - resolution: "is-subset@npm:0.1.1" - checksum: 97b8d7852af165269b7495095691a6ce6cf20bdfa1f846f97b4560ee190069686107af4e277fbd93aa0845c4d5db704391460ff6e9014aeb73264ba87893df44 - languageName: node - linkType: hard - "is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": version: 1.0.4 resolution: "is-symbol@npm:1.0.4" @@ -26572,20 +26362,6 @@ __metadata: languageName: node linkType: hard -"lodash.escape@npm:^4.0.1": - version: 4.0.1 - resolution: "lodash.escape@npm:4.0.1" - checksum: fcb54f457497256964d619d5cccbd80a961916fca60df3fe0fa3e7f052715c2944c0ed5aefb4f9e047d127d44aa2d55555f3350cb42c6549e9e293fb30b41e7f - languageName: node - linkType: hard - -"lodash.flattendeep@npm:^4.4.0": - version: 4.4.0 - resolution: "lodash.flattendeep@npm:4.4.0" - checksum: 8521c919acac3d4bcf0aaf040c1ca9cb35d6c617e2d72e9b4d51c9a58b4366622cd6077441a18be626c3f7b28227502b3bf042903d447b056ee7e0b11d45c722 - languageName: node - linkType: hard - "lodash.get@npm:^4.4.2": version: 4.4.2 resolution: "lodash.get@npm:4.4.2" @@ -26593,7 +26369,7 @@ __metadata: languageName: node linkType: hard -"lodash.isequal@npm:^4.0.0, lodash.isequal@npm:^4.5.0": +"lodash.isequal@npm:^4.0.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 @@ -28203,13 +27979,6 @@ __metadata: languageName: node linkType: hard -"moo@npm:^0.5.0": - version: 0.5.1 - resolution: "moo@npm:0.5.1" - checksum: 2d8c013f1f9aad8e5c7a9d4a03dbb4eecd91b9fe5e9446fbc7561fd38d4d161c742434acff385722542fe7b360fce9c586da62442379e62e4158ad49c7e1a6b7 - languageName: node - linkType: hard - "mousetrap-global-bind@npm:1.1.0": version: 1.1.0 resolution: "mousetrap-global-bind@npm:1.1.0" @@ -28434,23 +28203,6 @@ __metadata: languageName: node linkType: hard -"nearley@npm:^2.7.10": - version: 2.20.1 - resolution: "nearley@npm:2.20.1" - dependencies: - commander: ^2.19.0 - moo: ^0.5.0 - railroad-diagrams: ^1.0.0 - randexp: 0.4.6 - bin: - nearley-railroad: bin/nearley-railroad.js - nearley-test: bin/nearley-test.js - nearley-unparse: bin/nearley-unparse.js - nearleyc: bin/nearleyc.js - checksum: 42c2c330c13c7991b48221c5df00f4352c2f8851636ae4d1f8ca3c8e193fc1b7668c78011d1cad88cca4c1c4dc087425420629c19cc286d7598ec15533aaef26 - languageName: node - linkType: hard - "needle@npm:^2.5.2": version: 2.9.1 resolution: "needle@npm:2.9.1" @@ -29068,7 +28820,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.11.0, object-inspect@npm:^1.7.0, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.11.0, object-inspect@npm:^1.9.0": version: 1.11.0 resolution: "object-inspect@npm:1.11.0" checksum: 8c64f89ce3a7b96b6925879ad5f6af71d498abc217e136660efecd97452991216f375a7eb47cb1cb50643df939bf0c7cc391567b7abc6a924d04679705e58e27 @@ -29089,7 +28841,7 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.0.1, object-is@npm:^1.0.2, object-is@npm:^1.1.2, object-is@npm:^1.1.5": +"object-is@npm:^1.0.1, object-is@npm:^1.1.5": version: 1.1.5 resolution: "object-is@npm:1.1.5" dependencies: @@ -29150,7 +28902,7 @@ __metadata: languageName: node linkType: hard -"object.entries@npm:^1.1.1, object.entries@npm:^1.1.5": +"object.entries@npm:^1.1.5": version: 1.1.5 resolution: "object.entries@npm:1.1.5" dependencies: @@ -29161,7 +28913,7 @@ __metadata: languageName: node linkType: hard -"object.fromentries@npm:^2.0.0, object.fromentries@npm:^2.0.0 || ^1.0.0, object.fromentries@npm:^2.0.5": +"object.fromentries@npm:^2.0.0 || ^1.0.0, object.fromentries@npm:^2.0.5": version: 2.0.5 resolution: "object.fromentries@npm:2.0.5" dependencies: @@ -29235,7 +28987,7 @@ __metadata: languageName: node linkType: hard -"object.values@npm:^1.1.1, object.values@npm:^1.1.5": +"object.values@npm:^1.1.5": version: 1.1.5 resolution: "object.values@npm:1.1.5" dependencies: @@ -29842,15 +29594,6 @@ __metadata: languageName: node linkType: hard -"parse5-htmlparser2-tree-adapter@npm:^6.0.1": - version: 6.0.1 - resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1" - dependencies: - parse5: ^6.0.1 - checksum: 1848378b355d027915645c13f13f982e60502d201f53bc2067a508bf2dba4aac08219fc781dcd160167f5f50f0c73f58d20fa4fb3d90ee46762c20234fa90a6d - languageName: node - linkType: hard - "parse5-htmlparser2-tree-adapter@npm:^7.0.0": version: 7.0.0 resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0" @@ -31604,7 +31347,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:15.x, prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.4, prop-types@npm:^15.5.7, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2": +"prop-types@npm:15.x, prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.4, prop-types@npm:^15.5.7, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2": version: 15.7.2 resolution: "prop-types@npm:15.7.2" dependencies: @@ -31871,13 +31614,6 @@ __metadata: languageName: node linkType: hard -"railroad-diagrams@npm:^1.0.0": - version: 1.0.0 - resolution: "railroad-diagrams@npm:1.0.0" - checksum: 9e312af352b5ed89c2118edc0c06cef2cc039681817f65266719606e4e91ff6ae5374c707cc9033fe29a82c2703edf3c63471664f97f0167c85daf6f93496319 - languageName: node - linkType: hard - "ramda@npm:^0.28.0": version: 0.28.0 resolution: "ramda@npm:0.28.0" @@ -31885,16 +31621,6 @@ __metadata: languageName: node linkType: hard -"randexp@npm:0.4.6": - version: 0.4.6 - resolution: "randexp@npm:0.4.6" - dependencies: - discontinuous-range: 1.0.0 - ret: ~0.1.10 - checksum: 3c0d440a3f89d6d36844aa4dd57b5cdb0cab938a41956a16da743d3a3578ab32538fc41c16cc0984b6938f2ae4cbc0216967e9829e52191f70e32690d8e3445d - languageName: node - linkType: hard - "randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -32756,7 +32482,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:17.0.2, react-is@npm:^16.12.0 || ^17.0.0, react-is@npm:^17.0.0, react-is@npm:^17.0.1, react-is@npm:^17.0.2": +"react-is@npm:17.0.2, react-is@npm:^16.12.0 || ^17.0.0, react-is@npm:^17.0.1, react-is@npm:^17.0.2": version: 17.0.2 resolution: "react-is@npm:17.0.2" checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8 @@ -33066,7 +32792,7 @@ __metadata: languageName: node linkType: hard -"react-test-renderer@npm:17.0.2, react-test-renderer@npm:^17.0.0": +"react-test-renderer@npm:17.0.2": version: 17.0.2 resolution: "react-test-renderer@npm:17.0.2" dependencies: @@ -34291,16 +34017,6 @@ __metadata: languageName: node linkType: hard -"rst-selector-parser@npm:^2.2.3": - version: 2.2.3 - resolution: "rst-selector-parser@npm:2.2.3" - dependencies: - lodash.flattendeep: ^4.4.0 - nearley: ^2.7.10 - checksum: fbfb2f6a7d4c9b3e013ef555ac06e5dba444e0d37dc959b94c507b6c34093ef10fe98141338d9cac58e5ae0f9453a5ef7f85af3d5e6386b237c1b3552debe4a0 - languageName: node - linkType: hard - "rst2html@github:thoward/rst2html#990cb89f2a300cdd9151790be377c4c0840df809": version: 1.0.4 resolution: "rst2html@https://github.com/thoward/rst2html.git#commit=990cb89f2a300cdd9151790be377c4c0840df809" @@ -36018,17 +35734,6 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.1": - version: 1.2.5 - resolution: "string.prototype.trim@npm:1.2.5" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: d9f748ffca2a3ce722c421f7c2993b6490ec0cf19d9cb0904598c744e9367e54a3f13c7b99c8c0966c8a76484bd656a60281daa5d0534cc222cd72193fd63034 - languageName: node - linkType: hard - "string.prototype.trimend@npm:^1.0.4": version: 1.0.4 resolution: "string.prototype.trimend@npm:1.0.4" @@ -37411,7 +37116,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1": +"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1": version: 2.3.1 resolution: "tslib@npm:2.3.1" checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 From 4167214e3535f41c85004c6aca86fcb9ae8956dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Bedi?= Date: Tue, 24 Jan 2023 10:43:44 +0100 Subject: [PATCH 26/46] Panel edit: Add feature to drag & drop spreadsheet files to the grafana datasource (#60586) Co-authored-by: Oscar Kilhed Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> Co-authored-by: Adela Almasan --- .../feature-toggles/index.md | 1 + package.json | 3 +- .../src/types/featureToggles.gen.ts | 1 + pkg/services/featuremgmt/registry.go | 6 + pkg/services/featuremgmt/toggles_gen.go | 4 + public/app/core/utils/sheet.ts | 12 ++ .../grafana/components/QueryEditor.tsx | 117 ++++++++++++++++-- .../app/plugins/datasource/grafana/types.ts | 6 + yarn.lock | 10 ++ 9 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 public/app/core/utils/sheet.ts diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index c1fdca1d1c1..8fd69ec9ccb 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -95,6 +95,7 @@ Alpha features might be changed or removed without prior notice. | `authnService` | Use new auth service to perform authentication | | `sessionRemoteCache` | Enable using remote cache for user sessions | | `alertingBacktesting` | Rule backtesting API for alerting | +| `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files | | `azureMultipleResourcePicker` | Azure multiple resource picker | ## Development feature toggles diff --git a/package.json b/package.json index 23a3f9bcd2b..99c2f4ef579 100644 --- a/package.json +++ b/package.json @@ -404,7 +404,8 @@ "uuid": "9.0.0", "vendor": "link:./public/vendor", "visjs-network": "4.25.0", - "whatwg-fetch": "3.6.2" + "whatwg-fetch": "3.6.2", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz" }, "resolutions": { "underscore": "1.13.6", diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index e19fb2fe59f..f46074443ac 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -87,6 +87,7 @@ export interface FeatureToggles { sessionRemoteCache?: boolean; disablePrometheusExemplarSampling?: boolean; alertingBacktesting?: boolean; + editPanelCSVDragAndDrop?: boolean; alertingNoNormalState?: boolean; azureMultipleResourcePicker?: boolean; } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 06927320c33..1dc085f4b6d 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -397,6 +397,12 @@ var ( Description: "Rule backtesting API for alerting", State: FeatureStateAlpha, }, + { + Name: "editPanelCSVDragAndDrop", + Description: "Enables drag and drop for CSV and Excel files", + FrontendOnly: true, + State: FeatureStateAlpha, + }, { Name: "alertingNoNormalState", Description: "Stop maintaining state of alerts that are not firing", diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index af44efff9a3..b2915a9f32a 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -291,6 +291,10 @@ const ( // Rule backtesting API for alerting FlagAlertingBacktesting = "alertingBacktesting" + // FlagEditPanelCSVDragAndDrop + // Enables drag and drop for CSV and Excel files + FlagEditPanelCSVDragAndDrop = "editPanelCSVDragAndDrop" + // FlagAlertingNoNormalState // Stop maintaining state of alerts that are not firing FlagAlertingNoNormalState = "alertingNoNormalState" diff --git a/public/app/core/utils/sheet.ts b/public/app/core/utils/sheet.ts new file mode 100644 index 00000000000..5771b772283 --- /dev/null +++ b/public/app/core/utils/sheet.ts @@ -0,0 +1,12 @@ +import { read, utils } from 'xlsx'; + +import { ArrayDataFrame, DataFrame } from '@grafana/data'; + +export function readSpreadsheet(file: ArrayBuffer): DataFrame[] { + const wb = read(file, { type: 'buffer' }); + return wb.SheetNames.map((name) => { + const frame = new ArrayDataFrame(utils.sheet_to_json(wb.Sheets[name])); + frame.name = name; + return frame; + }); +} diff --git a/public/app/plugins/datasource/grafana/components/QueryEditor.tsx b/public/app/plugins/datasource/grafana/components/QueryEditor.tsx index aae44eac03b..3956c836a19 100644 --- a/public/app/plugins/datasource/grafana/components/QueryEditor.tsx +++ b/public/app/plugins/datasource/grafana/components/QueryEditor.tsx @@ -1,3 +1,4 @@ +import { css } from '@emotion/css'; import pluralize from 'pluralize'; import React, { PureComponent } from 'react'; @@ -8,10 +9,27 @@ import { rangeUtil, DataQueryRequest, DataFrame, + DataFrameJSON, + dataFrameToJSON, + GrafanaTheme2, + getValueFormat, + formattedValueToString, } from '@grafana/data'; import { config, getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; -import { InlineField, Select, Alert, Input, InlineFieldRow, InlineLabel } from '@grafana/ui'; +import { + InlineField, + Select, + Alert, + Input, + InlineFieldRow, + InlineLabel, + FileDropzone, + DropzoneFile, + Themeable2, + withTheme2, +} from '@grafana/ui'; import { hasAlphaPanels } from 'app/core/config'; +import { readSpreadsheet } from 'app/core/utils/sheet'; import { SearchQuery } from 'app/features/search/service'; import { GrafanaDatasource } from '../datasource'; @@ -19,7 +37,7 @@ import { defaultQuery, GrafanaQuery, GrafanaQueryType } from '../types'; import SearchEditor from './SearchEditor'; -type Props = QueryEditorProps; +interface Props extends QueryEditorProps, Themeable2 {} const labelWidth = 12; @@ -29,7 +47,7 @@ interface State { folders?: Array>; } -export class QueryEditor extends PureComponent { +export class UnthemedQueryEditor extends PureComponent { state: State = { channels: [], channelFields: {} }; queryTypes: Array> = [ @@ -60,6 +78,13 @@ export class QueryEditor extends PureComponent { description: 'Search for grafana resources', }); } + if (config.featureToggles.editPanelCSVDragAndDrop) { + this.queryTypes.push({ + label: 'Spreadsheet or snapshot', + value: GrafanaQueryType.Snapshot, + description: 'Query an uploaded spreadsheet or a snapshot', + }); + } } loadChannelInfo() { @@ -345,15 +370,47 @@ export class QueryEditor extends PureComponent { ); } + // Skip rendering the file list as we're handling that in this component instead. + fileListRenderer = (file: DropzoneFile, removeFile: (file: DropzoneFile) => void) => { + return null; + }; + + onDropAccepted = (files: File[]) => { + this.props.onChange({ ...this.props.query, file: { name: files[0].name, size: files[0].size } }); + }; + renderSnapshotQuery() { - const { query } = this.props; + const { query, theme } = this.props; + const file = query.file; + const styles = getStyles(theme); + const fileSize = getValueFormat('decbytes')(file ? file.size : 0); return ( - - - {pluralize('frame', query.snapshot?.length ?? 0, true)} - - + <> + + + {pluralize('frame', query.snapshot?.length ?? 0, true)} + + + {config.featureToggles.editPanelCSVDragAndDrop && ( + <> + + {file && ( +
+ {file?.name} + + {formattedValueToString(fileSize)} + +
+ )} + + )} + ); } @@ -367,6 +424,28 @@ export class QueryEditor extends PureComponent { onRunQuery(); }; + onFileDrop = (result: ArrayBuffer | String | null) => { + const snapshot: DataFrameJSON[] = []; + + if (result) { + if (!result || result instanceof String) { + return; + } + const dataFrames = readSpreadsheet(result); + dataFrames.forEach((df) => { + const dataframeJson = dataFrameToJSON(df); + snapshot.push(dataframeJson); + }); + } + + this.props.onChange({ + ...this.props.query, + queryType: GrafanaQueryType.Snapshot, + snapshot, + }); + this.props.onRunQuery(); + }; + render() { const query = { ...defaultQuery, @@ -377,7 +456,7 @@ export class QueryEditor extends PureComponent { // Only show "snapshot" when it already exists let queryTypes = this.queryTypes; - if (queryType === GrafanaQueryType.Snapshot) { + if (queryType === GrafanaQueryType.Snapshot && !config.featureToggles.editPanelCSVDragAndDrop) { queryTypes = [ ...this.queryTypes, { @@ -414,3 +493,21 @@ export class QueryEditor extends PureComponent { ); } } + +export const QueryEditor = withTheme2(UnthemedQueryEditor); + +function getStyles(theme: GrafanaTheme2) { + return { + file: css` + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: ${theme.spacing(2)}; + border: 1px dashed ${theme.colors.border.medium}; + background-color: ${theme.colors.background.secondary}; + margin-top: ${theme.spacing(1)}; + `, + }; +} diff --git a/public/app/plugins/datasource/grafana/types.ts b/public/app/plugins/datasource/grafana/types.ts index 55988f8fd81..435b03a9c22 100644 --- a/public/app/plugins/datasource/grafana/types.ts +++ b/public/app/plugins/datasource/grafana/types.ts @@ -26,6 +26,12 @@ export interface GrafanaQuery extends DataQuery { path?: string; // for list and read search?: SearchQuery; snapshot?: DataFrameJSON[]; + file?: GrafanaQueryFile; +} + +export interface GrafanaQueryFile { + name: string; + size: number; } export const defaultQuery: GrafanaQuery = { diff --git a/yarn.lock b/yarn.lock index 95d070c1c31..7750c3aa7f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21903,6 +21903,7 @@ __metadata: webpack-manifest-plugin: 5.0.0 webpack-merge: 5.8.0 whatwg-fetch: 3.6.2 + xlsx: "https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz" languageName: unknown linkType: soft @@ -39179,6 +39180,15 @@ __metadata: languageName: node linkType: hard +"xlsx@https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz": + version: 0.19.1 + resolution: "xlsx@https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz" + bin: + xlsx: ./bin/xlsx.njs + checksum: a7fa1b95dfc9a6923458e19f0dcd0c64d70ce49a5959c8f38c219fd232a4fdfd48d70115dbe01b0e58e80f336ce872495dcc3d12943c4d19b041c87f8eeb69c8 + languageName: node + linkType: hard + "xml-name-validator@npm:^3.0.0": version: 3.0.0 resolution: "xml-name-validator@npm:3.0.0" From 81c35560a8434df3f9ebe594410db991ccd86cfe Mon Sep 17 00:00:00 2001 From: Andre Pereira Date: Tue, 24 Jan 2023 11:08:40 +0000 Subject: [PATCH 27/46] Chore: Update the tempo devenv with the latest Tempo config changes (#61622) * Update the tempo devenv with the latest Tempo config changes * Remove some settings to instead use the default values * Pin the Tempo image to the current latest version Co-authored-by: Hamas Shafiq --- devenv/docker/blocks/tempo/docker-compose.yaml | 2 +- devenv/docker/blocks/tempo/tempo.yaml | 18 +++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/devenv/docker/blocks/tempo/docker-compose.yaml b/devenv/docker/blocks/tempo/docker-compose.yaml index 495a81e92aa..82915188e09 100644 --- a/devenv/docker/blocks/tempo/docker-compose.yaml +++ b/devenv/docker/blocks/tempo/docker-compose.yaml @@ -67,7 +67,7 @@ services: logging: *default-logging tempo: - image: grafana/tempo:latest + image: grafana/tempo:main-9a8474f command: - --config.file=/etc/tempo.yaml - --search.enabled=true diff --git a/devenv/docker/blocks/tempo/tempo.yaml b/devenv/docker/blocks/tempo/tempo.yaml index 5dfa486d345..168e6a3cc81 100644 --- a/devenv/docker/blocks/tempo/tempo.yaml +++ b/devenv/docker/blocks/tempo/tempo.yaml @@ -18,11 +18,6 @@ distributor: grpc: opencensus: -ingester: - trace_idle_period: 10s # the length of time after a trace has not received spans to consider it complete and flush it - max_block_bytes: 1_000_000 # cut the head block when it hits this size or ... - max_block_duration: 5m # this much time passes - compactor: compaction: compaction_window: 1h # blocks in this time window will be compacted together @@ -41,26 +36,19 @@ metrics_generator: - url: http://prometheus:9090/api/v1/write send_exemplars: true -query_frontend: - search: - max_duration: 0 # allow searches >1h - storage: trace: backend: local # backend configuration to use block: bloom_filter_false_positive: .05 # bloom filter false positive rate. lower values create larger filters but fewer false positives - index_downsample_bytes: 1000 # number of bytes per index record - encoding: zstd # block encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2 + v2_index_downsample_bytes: 1000 # number of bytes per index record + v2_encoding: zstd # block encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2 version: vParquet wal: path: /tmp/tempo/wal # where to store the the wal locally - encoding: snappy # wal encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2 + v2_encoding: snappy # wal encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2 local: path: /tmp/tempo/blocks - pool: - max_workers: 100 # worker pool determines the number of parallel requests to the object store backend - queue_depth: 10000 overrides: metrics_generator_processors: [service-graphs, span-metrics] From 6c9d9a2db5a13ba3bf9ec0237b8926353ef6debc Mon Sep 17 00:00:00 2001 From: Oscar Kilhed Date: Tue, 24 Jan 2023 12:42:40 +0100 Subject: [PATCH 28/46] Table panel: Use link elements instead of div elements with on click events to aid with keyboard accessibility (#59393) * TablePanel: fix image of image cell overflowing table cell when a data link is added --- .../src/components/Button/Button.tsx | 15 +++++++++++++++ .../src/components/Table/DefaultCell.tsx | 18 +++++++++++++----- .../src/components/Table/FilterPopup.tsx | 2 ++ .../src/components/Table/ImageCell.tsx | 18 +++++++++++++----- .../src/components/Table/JSONViewCell.tsx | 17 ++++++++++++----- 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/packages/grafana-ui/src/components/Button/Button.tsx b/packages/grafana-ui/src/components/Button/Button.tsx index ae3a9032956..711b42dcd74 100644 --- a/packages/grafana-ui/src/components/Button/Button.tsx +++ b/packages/grafana-ui/src/components/Button/Button.tsx @@ -311,3 +311,18 @@ export const clearButtonStyles = (theme: GrafanaTheme2) => { padding: 0; `; }; + +export const clearLinkButtonStyles = (theme: GrafanaTheme2) => { + return css` + background: transparent; + border: none; + padding: 0; + font-family: inherit; + color: inherit; + height: 100%; + &:hover { + background: transparent; + color: inherit; + } + `; +}; diff --git a/packages/grafana-ui/src/components/Table/DefaultCell.tsx b/packages/grafana-ui/src/components/Table/DefaultCell.tsx index 026cfb16e8a..7f134b3056a 100644 --- a/packages/grafana-ui/src/components/Table/DefaultCell.tsx +++ b/packages/grafana-ui/src/components/Table/DefaultCell.tsx @@ -5,7 +5,9 @@ import tinycolor from 'tinycolor2'; import { DisplayValue, formattedValueToString } from '@grafana/data'; import { TableCellBackgroundDisplayMode, TableCellOptions } from '@grafana/schema'; +import { useStyles2 } from '../../themes'; import { getCellLinks, getTextColorForAlphaBackground } from '../../utils'; +import { Button, clearLinkButtonStyles } from '../Button'; import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu'; import { CellActions } from './CellActions'; @@ -31,6 +33,7 @@ export const DefaultCell: FC = (props) => { const cellOptions = getCellOptions(field); const cellStyle = getCellStyle(tableStyles, cellOptions, displayValue, inspectEnabled); const hasLinks = Boolean(getCellLinks(field, row)?.length); + const clearButtonStyle = useStyles2(clearLinkButtonStyles); return (
@@ -39,11 +42,16 @@ export const DefaultCell: FC = (props) => { {hasLinks && ( getCellLinks(field, row) || []}> {(api) => { - return ( -
- {value} -
- ); + const content =
{value}
; + if (api.openMenu) { + return ( + + ); + } else { + return content; + } }}
)} diff --git a/packages/grafana-ui/src/components/Table/FilterPopup.tsx b/packages/grafana-ui/src/components/Table/FilterPopup.tsx index 7261e66ee96..3498b0b3700 100644 --- a/packages/grafana-ui/src/components/Table/FilterPopup.tsx +++ b/packages/grafana-ui/src/components/Table/FilterPopup.tsx @@ -50,6 +50,8 @@ export const FilterPopup: FC = ({ column: { preFilteredRows, filterValue, return ( + {/* This is just blocking click events from bubbeling and should not have a keyboard interaction. */} + {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
diff --git a/packages/grafana-ui/src/components/Table/ImageCell.tsx b/packages/grafana-ui/src/components/Table/ImageCell.tsx index c1e8b2ee2f1..9dffa16b88e 100644 --- a/packages/grafana-ui/src/components/Table/ImageCell.tsx +++ b/packages/grafana-ui/src/components/Table/ImageCell.tsx @@ -1,7 +1,9 @@ import { cx } from '@emotion/css'; import React, { FC } from 'react'; +import { useStyles2 } from '../../themes'; import { getCellLinks } from '../../utils'; +import { Button, clearLinkButtonStyles } from '../Button'; import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu'; import { TableCellProps } from './types'; @@ -12,6 +14,7 @@ export const ImageCell: FC = (props) => { const displayValue = field.display!(cell.value); const hasLinks = Boolean(getCellLinks(field, row)?.length); + const clearButtonStyle = useStyles2(clearLinkButtonStyles); return (
@@ -19,11 +22,16 @@ export const ImageCell: FC = (props) => { {hasLinks && ( getCellLinks(field, row) || []}> {(api) => { - return ( -
- -
- ); + const img = ; + if (api.openMenu) { + return ( + + ); + } else { + return img; + } }}
)} diff --git a/packages/grafana-ui/src/components/Table/JSONViewCell.tsx b/packages/grafana-ui/src/components/Table/JSONViewCell.tsx index 70c73fce64e..68483d62e19 100644 --- a/packages/grafana-ui/src/components/Table/JSONViewCell.tsx +++ b/packages/grafana-ui/src/components/Table/JSONViewCell.tsx @@ -2,7 +2,9 @@ import { css, cx } from '@emotion/css'; import { isString } from 'lodash'; import React from 'react'; +import { useStyles2 } from '../../themes'; import { getCellLinks } from '../../utils'; +import { Button, clearLinkButtonStyles } from '../Button'; import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu'; import { CellActions } from './CellActions'; @@ -28,6 +30,7 @@ export function JSONViewCell(props: TableCellProps): JSX.Element { } const hasLinks = Boolean(getCellLinks(field, row)?.length); + const clearButtonStyle = useStyles2(clearLinkButtonStyles); return (
@@ -36,11 +39,15 @@ export function JSONViewCell(props: TableCellProps): JSX.Element { {hasLinks && ( getCellLinks(field, row) || []}> {(api) => { - return ( -
- {displayValue} -
- ); + if (api.openMenu) { + return ( + + ); + } else { + return <>{displayValue}; + } }}
)} From 18e0a060e6994fa64090cfa194f14ec875582af6 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Tue, 24 Jan 2023 13:46:35 +0200 Subject: [PATCH 29/46] AlertGroups: Generate models.gen.ts from models.cue (#61227) * AlertGroups: Generate models.gen.ts from models.cue * Update structure * Update report * Fix filenames * Add missing file --- .../alertgroupspanelcfg/schema-reference.md | 30 ++++++++++++++++ pkg/kindsys/report.json | 14 ++++---- .../alertGroups/AlertGroupsPanel.test.tsx | 8 ++--- .../panel/alertGroups/AlertGroupsPanel.tsx | 4 +-- .../app/plugins/panel/alertGroups/module.tsx | 4 +-- .../plugins/panel/alertGroups/panelcfg.cue | 36 +++++++++++++++++++ .../plugins/panel/alertGroups/panelcfg.gen.ts | 26 ++++++++++++++ public/app/plugins/panel/alertGroups/types.ts | 5 --- public/app/plugins/panel/geomap/models.cue | 28 +++++++-------- 9 files changed, 121 insertions(+), 34 deletions(-) create mode 100644 docs/sources/developers/kinds/composable/alertgroupspanelcfg/schema-reference.md create mode 100644 public/app/plugins/panel/alertGroups/panelcfg.cue create mode 100644 public/app/plugins/panel/alertGroups/panelcfg.gen.ts delete mode 100644 public/app/plugins/panel/alertGroups/types.ts diff --git a/docs/sources/developers/kinds/composable/alertgroupspanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/alertgroupspanelcfg/schema-reference.md new file mode 100644 index 00000000000..95333a13e8e --- /dev/null +++ b/docs/sources/developers/kinds/composable/alertgroupspanelcfg/schema-reference.md @@ -0,0 +1,30 @@ +--- +keywords: + - grafana + - schema +title: AlertGroupsPanelCfg kind +--- +> Both documentation generation and kinds schemas are in active development and subject to change without prior notice. + +# AlertGroupsPanelCfg kind + +### Maturity: merged +### Version: 0.0 + +## Properties + +| Property | Type | Required | Description | +|----------------|-------------------------|----------|-------------| +| `PanelOptions` | [object](#paneloptions) | **Yes** | | + +## PanelOptions + +### Properties + +| Property | Type | Required | Description | +|----------------|---------|----------|-------------------------------------------------------------| +| `alertmanager` | string | **Yes** | Name of the alertmanager used as a source for alerts | +| `expandAll` | boolean | **Yes** | Expand all alert groups by default | +| `labels` | string | **Yes** | Comma-separated list of values used to filter alert results | + + diff --git a/pkg/kindsys/report.json b/pkg/kindsys/report.json index 5878ac71d90..cd7bc62cec8 100644 --- a/pkg/kindsys/report.json +++ b/pkg/kindsys/report.json @@ -9,13 +9,13 @@ "grafanaMaturityCount": 0, "lineageIsGroup": true, "links": { - "docs": "n/a", + "docs": "https:/grafana.com/docs/grafana/next/developers/kinds/composable/alertgroupspanelcfg/schema-reference", "go": "n/a", - "schema": "n/a", - "ts": "n/a" + "schema": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/alertGroups/panelcfg.cue", + "ts": "https:/github.com/grafana/grafana/tree/main/public/app/plugins/panel/alertGroups/panelcfg.gen.ts" }, "machineName": "alertgroupspanelcfg", - "maturity": "planned", + "maturity": "merged", "name": "AlertGroupsPanelCfg", "pluralMachineName": "alertgroupspanelcfgs", "pluralName": "AlertGroupsPanelCfgs", @@ -1659,15 +1659,15 @@ "merged": { "name": "merged", "items": [ + "alertgroupspanelcfg", "playlist", "team" ], - "count": 2 + "count": 3 }, "planned": { "name": "planned", "items": [ - "alertgroupspanelcfg", "alertlistpanelcfg", "alertmanagerdataquery", "alertmanagerdatasourcecfg", @@ -1729,7 +1729,7 @@ "zipkindataquery", "zipkindatasourcecfg" ], - "count": 61 + "count": 60 }, "stable": { "name": "stable", diff --git a/public/app/plugins/panel/alertGroups/AlertGroupsPanel.test.tsx b/public/app/plugins/panel/alertGroups/AlertGroupsPanel.test.tsx index fb373206a47..c1201a53426 100644 --- a/public/app/plugins/panel/alertGroups/AlertGroupsPanel.test.tsx +++ b/public/app/plugins/panel/alertGroups/AlertGroupsPanel.test.tsx @@ -17,7 +17,7 @@ import { setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { configureStore } from 'app/store/configureStore'; import { AlertGroupsPanel } from './AlertGroupsPanel'; -import { AlertGroupPanelOptions } from './types'; +import { PanelOptions } from './panelcfg.gen'; jest.mock('app/features/alerting/unified/api/alertmanager'); @@ -44,13 +44,13 @@ const dataSources = { }), }; -const defaultOptions: AlertGroupPanelOptions = { +const defaultOptions: PanelOptions = { labels: '', alertmanager: 'Alertmanager', expandAll: false, }; -const defaultProps: PanelProps = { +const defaultProps: PanelProps = { data: { state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() }, id: 1, timeRange: getDefaultTimeRange(), @@ -78,7 +78,7 @@ const defaultProps: PanelProps = { width: 320, }; -const renderPanel = (options: AlertGroupPanelOptions = defaultOptions) => { +const renderPanel = (options: PanelOptions = defaultOptions) => { const store = configureStore(); const dash: any = { id: 1, formatDate: (time: number) => new Date(time).toISOString() }; const dashSrv: any = { getCurrent: () => dash }; diff --git a/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx b/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx index 9ec750e543c..00badfa982e 100644 --- a/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx +++ b/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx @@ -12,10 +12,10 @@ import { AlertmanagerGroup, Matcher } from 'app/plugins/datasource/alertmanager/ import { useDispatch } from 'app/types'; import { AlertGroup } from './AlertGroup'; -import { AlertGroupPanelOptions } from './types'; +import { PanelOptions } from './panelcfg.gen'; import { useFilteredGroups } from './useFilteredGroups'; -export const AlertGroupsPanel = (props: PanelProps) => { +export const AlertGroupsPanel = (props: PanelProps) => { const dispatch = useDispatch(); const isAlertingEnabled = config.unifiedAlertingEnabled; diff --git a/public/app/plugins/panel/alertGroups/module.tsx b/public/app/plugins/panel/alertGroups/module.tsx index b2e37a74ff9..66aeabd22c1 100644 --- a/public/app/plugins/panel/alertGroups/module.tsx +++ b/public/app/plugins/panel/alertGroups/module.tsx @@ -8,9 +8,9 @@ import { } from 'app/features/alerting/unified/utils/datasource'; import { AlertGroupsPanel } from './AlertGroupsPanel'; -import { AlertGroupPanelOptions } from './types'; +import { PanelOptions } from './panelcfg.gen'; -export const plugin = new PanelPlugin(AlertGroupsPanel).setPanelOptions((builder) => { +export const plugin = new PanelPlugin(AlertGroupsPanel).setPanelOptions((builder) => { return builder .addCustomEditor({ name: 'Alertmanager', diff --git a/public/app/plugins/panel/alertGroups/panelcfg.cue b/public/app/plugins/panel/alertGroups/panelcfg.cue new file mode 100644 index 00000000000..009ff732fdf --- /dev/null +++ b/public/app/plugins/panel/alertGroups/panelcfg.cue @@ -0,0 +1,36 @@ +// Copyright 2023 Grafana Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package grafanaplugin + +composableKinds: PanelCfg: { + lineage: { + seqs: [ + { + schemas: [ + { + PanelOptions: { + // Comma-separated list of values used to filter alert results + labels: string + // Name of the alertmanager used as a source for alerts + alertmanager: string + // Expand all alert groups by default + expandAll: bool + } @cuetsy(kind="interface") + }, + ] + }, + ] + } +} diff --git a/public/app/plugins/panel/alertGroups/panelcfg.gen.ts b/public/app/plugins/panel/alertGroups/panelcfg.gen.ts new file mode 100644 index 00000000000..d7543479587 --- /dev/null +++ b/public/app/plugins/panel/alertGroups/panelcfg.gen.ts @@ -0,0 +1,26 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. +// +// Generated by: +// public/app/plugins/gen.go +// Using jennies: +// TSTypesJenny +// PluginTSTypesJenny +// +// Run 'make gen-cue' from repository root to regenerate. + +export const PanelCfgModelVersion = Object.freeze([0, 0]); + +export interface PanelOptions { + /** + * Name of the alertmanager used as a source for alerts + */ + alertmanager: string; + /** + * Expand all alert groups by default + */ + expandAll: boolean; + /** + * Comma-separated list of values used to filter alert results + */ + labels: string; +} diff --git a/public/app/plugins/panel/alertGroups/types.ts b/public/app/plugins/panel/alertGroups/types.ts deleted file mode 100644 index eec4309e8ab..00000000000 --- a/public/app/plugins/panel/alertGroups/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface AlertGroupPanelOptions { - labels: string; - alertmanager: string; - expandAll: boolean; -} diff --git a/public/app/plugins/panel/geomap/models.cue b/public/app/plugins/panel/geomap/models.cue index d502a796ee4..5d5fcff0ea8 100644 --- a/public/app/plugins/panel/geomap/models.cue +++ b/public/app/plugins/panel/geomap/models.cue @@ -16,7 +16,7 @@ package grafanaplugin import ( "github.com/grafana/thema" - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" + ui "github.com/grafana/grafana/packages/grafana-schema/src/common" ) Panel: thema.#Lineage & { @@ -26,25 +26,25 @@ Panel: thema.#Lineage & { schemas: [ { PanelOptions: { - view: MapViewConfig + view: MapViewConfig controls: ControlsOptions - basemap: ui.MapLayerOptions + basemap: ui.MapLayerOptions layers: [...ui.MapLayerOptions] tooltip: TooltipOptions } @cuetsy(kind="interface") MapViewConfig: { - id: string | *"zero" - lat?: int64 | *0 - lon?: int64 | *0 - zoom?: int64 | *1 - minZoom?: int64 - maxZoom?: int64 - padding?: int64 + id: string | *"zero" + lat?: int64 | *0 + lon?: int64 | *0 + zoom?: int64 | *1 + minZoom?: int64 + maxZoom?: int64 + padding?: int64 allLayers?: bool | *true - lastOnly?: bool - layer?: string - shared?: bool + lastOnly?: bool + layer?: string + shared?: bool } @cuetsy(kind="interface") ControlsOptions: { @@ -68,7 +68,7 @@ Panel: thema.#Lineage & { TooltipMode: "none" | "details" @cuetsy(kind="enum",memberNames="None|Details") - MapCenterID: "zero"|"coords"|"fit" @cuetsy(kind="enum",members="Zero|Coordinates|Fit") + MapCenterID: "zero" | "coords" | "fit" @cuetsy(kind="enum",members="Zero|Coordinates|Fit") }, ] }, From ec171bcad5cfc7cacf1444632086ffc1f0694f41 Mon Sep 17 00:00:00 2001 From: ying-jeanne <74549700+ying-jeanne@users.noreply.github.com> Date: Tue, 24 Jan 2023 19:57:33 +0800 Subject: [PATCH 30/46] [xorm] Clean up xorm dialect & cascade (#61969) clean up xorm dialect --- pkg/util/xorm/dialect_mysql.go | 27 +++--------- pkg/util/xorm/engine.go | 14 ------- pkg/util/xorm/session.go | 46 -------------------- pkg/util/xorm/session_convert.go | 72 +------------------------------- pkg/util/xorm/statement.go | 2 - 5 files changed, 7 insertions(+), 154 deletions(-) diff --git a/pkg/util/xorm/dialect_mysql.go b/pkg/util/xorm/dialect_mysql.go index cf1dbb6f214..5f167630d41 100644 --- a/pkg/util/xorm/dialect_mysql.go +++ b/pkg/util/xorm/dialect_mysql.go @@ -5,7 +5,6 @@ package xorm import ( - "crypto/tls" "errors" "fmt" "regexp" @@ -163,16 +162,7 @@ var ( type mysql struct { core.Base - net string - addr string - params map[string]string - loc *time.Location - timeout time.Duration - tls *tls.Config - allowAllFiles bool - allowOldPasswords bool - clientFoundRows bool - rowFormat string + rowFormat string } func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { @@ -192,7 +182,6 @@ func (db *mysql) SetParams(params map[string]string) { fallthrough case "COMPRESSED": db.rowFormat = t - break default: break } @@ -339,7 +328,7 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column } col.Name = strings.Trim(columnName, "` ") col.Comment = comment - if "YES" == isNullable { + if isNullable == "YES" { col.Nullable = true } @@ -397,15 +386,12 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column if _, ok := core.SqlTypes[colType]; ok { col.SQLType = core.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2} } else { - return nil, nil, fmt.Errorf("Unknown colType %v", colType) + return nil, nil, fmt.Errorf("unknown colType %v", colType) } if colKey == "PRI" { col.IsPrimaryKey = true } - if colKey == "UNI" { - // col.is - } if extra == "auto_increment" { col.IsAutoIncrement = true @@ -478,7 +464,7 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { continue } - if "YES" == nonUnique || nonUnique == "1" { + if nonUnique == "YES" || nonUnique == "1" { indexType = core.IndexType } else { indexType = core.UniqueType @@ -574,7 +560,7 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, err // Parse protocol part of URI p := strings.SplitN(pd[0], ":", 2) if len(p) != 2 { - return nil, errors.New("Wrong protocol part of URI") + return nil, errors.New("wrong protocol part of URI") } db.Proto = p[0] options := strings.Split(p[1], ",") @@ -606,7 +592,7 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, err // Parse database part of URI dup := strings.SplitN(pd[0], "/", 3) if len(dup) != 3 { - return nil, errors.New("Wrong database part of URI") + return nil, errors.New("wrong database part of URI") } db.DbName = dup[0] db.User = dup[1] @@ -625,7 +611,6 @@ func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error `\/(?P.*?)` + // /dbname `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] matches := dsnPattern.FindStringSubmatch(dataSourceName) - // tlsConfigRegister := make(map[string]*tls.Config) names := dsnPattern.SubexpNames() uri := &core.Uri{DbType: core.MYSQL} diff --git a/pkg/util/xorm/engine.go b/pkg/util/xorm/engine.go index dc91346f887..3c1267ae194 100644 --- a/pkg/util/xorm/engine.go +++ b/pkg/util/xorm/engine.go @@ -246,13 +246,6 @@ func (engine *Engine) NoCache() *Session { return session.NoCache() } -// NoCascade If you do not want to auto cascade load object -func (engine *Engine) NoCascade() *Session { - session := engine.NewSession() - session.isAutoClose = true - return session.NoCascade() -} - // NewDB provides an interface to operate database directly func (engine *Engine) NewDB() (*core.DB, error) { return core.OpenDialect(engine.dialect) @@ -375,13 +368,6 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { return tables, nil } -// Cascade use cascade or not -func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { - session := engine.NewSession() - session.isAutoClose = true - return session.Cascade(trueOrFalse...) -} - // Where method provide a condition query func (engine *Engine) Where(query interface{}, args ...interface{}) *Session { session := engine.NewSession() diff --git a/pkg/util/xorm/session.go b/pkg/util/xorm/session.go index 04c18028c91..60bffa3bf70 100644 --- a/pkg/util/xorm/session.go +++ b/pkg/util/xorm/session.go @@ -7,7 +7,6 @@ package xorm import ( "context" "database/sql" - "errors" "fmt" "hash/crc32" "reflect" @@ -144,12 +143,6 @@ func (session *Session) Alias(alias string) *Session { return session } -// NoCascade indicate that no cascade load child object -func (session *Session) NoCascade() *Session { - session.statement.UseCascade = false - return session -} - // ForUpdate Set Read/Write locking for UPDATE func (session *Session) ForUpdate() *Session { session.statement.IsForUpdate = true @@ -199,14 +192,6 @@ func (session *Session) Charset(charset string) *Session { return session } -// Cascade indicates if loading sub Struct -func (session *Session) Cascade(trueOrFalse ...bool) *Session { - if len(trueOrFalse) >= 1 { - session.statement.UseCascade = trueOrFalse[0] - } - return session -} - // MustLogSQL means record SQL or not and don't follow engine's setting func (session *Session) MustLogSQL(log ...bool) *Session { if len(log) > 0 { @@ -652,37 +637,6 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b fieldValue.Set(x.Elem()) } } - } else if session.statement.UseCascade { - table, err := session.engine.autoMapType(*fieldValue) - if err != nil { - return nil, err - } - - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - return nil, errors.New("unsupported non or composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - pk[0], err = asKind(vv, rawValueType) - if err != nil { - return nil, err - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - fieldValue.Set(structInter.Elem()) - } else { - return nil, errors.New("cascade obj is not exist") - } - } } case reflect.Ptr: // !nashtsai! TODO merge duplicated codes above diff --git a/pkg/util/xorm/session_convert.go b/pkg/util/xorm/session_convert.go index 2bbc248e99c..27d9a5e08d3 100644 --- a/pkg/util/xorm/session_convert.go +++ b/pkg/util/xorm/session_convert.go @@ -7,7 +7,6 @@ package xorm import ( "database/sql" "database/sql/driver" - "errors" "fmt" "reflect" "strconv" @@ -211,40 +210,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } v = x fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) - } else if session.statement.UseCascade { - table, err := session.engine.autoMapType(*fieldValue) - if err != nil { - return err - } - - // TODO: current only support 1 primary key - if len(table.PrimaryKeys) > 1 { - return errors.New("unsupported composited primary key cascade") - } - - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - pk[0], err = str2PK(string(data), rawValueType) - if err != nil { - return err - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Elem().Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist") - } - } } } case reflect.Ptr: @@ -493,42 +458,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, v = x fieldValue.Set(reflect.ValueOf(&x)) default: - if session.statement.UseCascade { - structInter := reflect.New(fieldType.Elem()) - table, err := session.engine.autoMapType(structInter.Elem()) - if err != nil { - return err - } - - if len(table.PrimaryKeys) > 1 { - return errors.New("unsupported composited primary key cascade") - } - - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - pk[0], err = str2PK(string(data), rawValueType) - if err != nil { - return err - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist") - } - } - } else { - return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) - } + return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) } default: return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) diff --git a/pkg/util/xorm/statement.go b/pkg/util/xorm/statement.go index 5c252529124..e952f25ced7 100644 --- a/pkg/util/xorm/statement.go +++ b/pkg/util/xorm/statement.go @@ -35,7 +35,6 @@ type Statement struct { tableName string RawSQL string RawParams []interface{} - UseCascade bool UseAutoJoin bool StoreEngine string Charset string @@ -66,7 +65,6 @@ func (statement *Statement) Init() { statement.Start = 0 statement.LimitN = nil statement.OrderStr = "" - statement.UseCascade = true statement.JoinStr = "" statement.joinArgs = make([]interface{}, 0) statement.GroupByStr = "" From 88347caf5fcc99c21c8a07367b9e846947367636 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 24 Jan 2023 12:41:09 +0000 Subject: [PATCH 31/46] Navigation: Open command palette from search box (#61667) * Reduce size of topnav search 'input' to 1/5th of the width, min width 200px * Open command palette on topnav search box click * Rename component * fix comment * feature flag the change * update feature flag description --- .../feature-toggles/index.md | 1 + .../src/types/featureToggles.gen.ts | 1 + pkg/services/featuremgmt/registry.go | 6 + pkg/services/featuremgmt/toggles_gen.go | 4 + .../components/AppChrome/TopSearchBar.tsx | 17 ++- .../TopSearchBarCommandPaletteTrigger.tsx | 109 ++++++++++++++++++ 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 8fd69ec9ccb..6dccb953693 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -46,6 +46,7 @@ Some stable features are enabled by default. You can disable a stable feature by | `datasourceLogger` | Logs all datasource requests | | `accessControlOnCall` | Access control primitives for OnCall | | `alertingNoNormalState` | Stop maintaining state of alerts that are not firing | +| `topNavCommandPalette` | Launch the Command Palette from the top navigation search box | ## Alpha feature toggles diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index f46074443ac..64b10278f96 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -90,4 +90,5 @@ export interface FeatureToggles { editPanelCSVDragAndDrop?: boolean; alertingNoNormalState?: boolean; azureMultipleResourcePicker?: boolean; + topNavCommandPalette?: boolean; } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 1dc085f4b6d..77d111cedf0 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -414,5 +414,11 @@ var ( Description: "Azure multiple resource picker", State: FeatureStateAlpha, }, + { + Name: "topNavCommandPalette", + Description: "Launch the Command Palette from the top navigation search box", + State: FeatureStateBeta, + FrontendOnly: true, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index b2915a9f32a..3ff5c49df92 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -302,4 +302,8 @@ const ( // FlagAzureMultipleResourcePicker // Azure multiple resource picker FlagAzureMultipleResourcePicker = "azureMultipleResourcePicker" + + // FlagTopNavCommandPalette + // Launch the Command Palette from the top navigation search box + FlagTopNavCommandPalette = "topNavCommandPalette" ) diff --git a/public/app/core/components/AppChrome/TopSearchBar.tsx b/public/app/core/components/AppChrome/TopSearchBar.tsx index b5a54d5b9e1..330eede2281 100644 --- a/public/app/core/components/AppChrome/TopSearchBar.tsx +++ b/public/app/core/components/AppChrome/TopSearchBar.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Dropdown, ToolbarButton, useStyles2 } from '@grafana/ui'; +import { config } from 'app/core/config'; import { contextSrv } from 'app/core/core'; import { useSelector } from 'app/types'; @@ -14,6 +15,7 @@ import { QuickAdd } from './QuickAdd/QuickAdd'; import { SignInLink } from './TopBar/SignInLink'; import { TopNavBarMenu } from './TopBar/TopNavBarMenu'; import { TopSearchBarSection } from './TopBar/TopSearchBarSection'; +import { TopSearchBarCommandPaletteTrigger } from './TopSearchBarCommandPaletteTrigger'; import { TopSearchBarInput } from './TopSearchBarInput'; import { TOP_BAR_LEVEL_HEIGHT } from './types'; @@ -24,6 +26,13 @@ export function TopSearchBar() { const helpNode = navIndex['help']; const profileNode = navIndex['profile']; + const search = + config.featureToggles.commandPalette && config.featureToggles.topNavcommandPalette ? ( + + ) : ( + + ); + return (
@@ -32,9 +41,9 @@ export function TopSearchBar() { - - - + + {search} + {helpNode && ( @@ -70,7 +79,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ justifyContent: 'space-between', [theme.breakpoints.up('sm')]: { - gridTemplateColumns: '1fr 1fr 1fr', + gridTemplateColumns: '2fr minmax(200px, 1fr) 2fr', // search should not be smaller than 200px display: 'grid', justifyContent: 'flex-start', diff --git a/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx b/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx new file mode 100644 index 00000000000..db5b77eecff --- /dev/null +++ b/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx @@ -0,0 +1,109 @@ +import { css } from '@emotion/css'; +import { useKBar, VisualState } from 'kbar'; +import React, { useState } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { getInputStyles, Icon, ToolbarButton, useStyles2, useTheme2 } from '@grafana/ui'; +import { focusCss } from '@grafana/ui/src/themes/mixins'; +import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange'; +import { t } from 'app/core/internationalization'; + +export function TopSearchBarCommandPaletteTrigger() { + const theme = useTheme2(); + const { query: kbar } = useKBar((kbarState) => ({ + kbarSearchQuery: kbarState.searchQuery, + kbarIsOpen: kbarState.visualState === VisualState.showing, + })); + + const breakpoint = theme.breakpoints.values.sm; + + const [isSmallScreen, setIsSmallScreen] = useState(window.matchMedia(`(max-width: ${breakpoint}px)`).matches); + + useMediaQueryChange({ + breakpoint, + onChange: (e) => { + setIsSmallScreen(e.matches); + }, + }); + + const onOpenSearch = () => { + kbar.toggle(); + }; + + if (isSmallScreen) { + return ( + + ); + } + + return ; +} + +function PretendTextInput({ onClick }: { onClick: () => void }) { + const styles = useStyles2(getStyles); + + // We want the desktop command palette trigger to look like a search box, + // but it actually behaves like a button - you active it and it performs an + // action. You don't actually type into it. + + return ( +
+
+
+ +
+ + +
+
+ ); +} + +const getStyles = (theme: GrafanaTheme2) => { + const baseStyles = getInputStyles({ theme }); + + return { + wrapper: baseStyles.wrapper, + inputWrapper: baseStyles.inputWrapper, + prefix: baseStyles.prefix, + fakeInput: css([ + baseStyles.input, + { + textAlign: 'left', + paddingLeft: 28, + color: theme.colors.text.disabled, + + // We want the focus styles to appear only when tabbing through, not when clicking the button + // (and when focus is restored after command palette closes) + '&:focus': { + outline: 'unset', + boxShadow: 'unset', + }, + + '&:focus-visible': css` + ${focusCss(theme)} + `, + }, + ]), + + button: css({ + // height: 32, + width: '100%', + textAlign: 'center', + + '> *': { + width: '100%', + textAlign: 'center', + justifyContent: 'center', + gap: '1ch', + }, + }), + }; +}; From d54cda62a3bd66f6d4c44e3eeeb65367b963b305 Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Tue, 24 Jan 2023 14:44:44 +0100 Subject: [PATCH 32/46] Add debug option for Golang tests in vscode (#61983) --- .vscode/launch.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index a6b91b2f521..792addee950 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,6 +27,14 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "port": 9229 + }, + { + "name": "Debug Go test", + "type": "go", + "request": "launch", + "mode": "test", + "program": "${workspaceFolder}/${relativeFileDirname}", + "showLog": true } ] } From 814e485dd3190f86f30bba7aaac7b5dff87f45c9 Mon Sep 17 00:00:00 2001 From: Isabella Siu Date: Tue, 24 Jan 2023 09:05:41 -0500 Subject: [PATCH 33/46] CloudWatch: Prevent log groups from being removed on query change (#61891) --- .../plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx index c520d8a2ea0..0df75f024f1 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx @@ -237,6 +237,7 @@ export default class LogsCheatSheet extends PureComponent< region: this.props.query.region, id: this.props.query.refId ?? 'A', logGroupNames: 'logGroupNames' in this.props.query ? this.props.query.logGroupNames : [], + logGroups: 'logGroups' in this.props.query ? this.props.query.logGroups : [], }) } > From 12a4a83c777675d8b01dd1ec4c3860e71ff797d7 Mon Sep 17 00:00:00 2001 From: Christopher Moyer <35463610+chri2547@users.noreply.github.com> Date: Tue, 24 Jan 2023 08:15:09 -0600 Subject: [PATCH 34/46] Docs: prose and format updates (#61720) * wording and format updates * corrects typo --- docs/sources/setup-grafana/upgrade-grafana.md | 163 ++++++++++++------ 1 file changed, 106 insertions(+), 57 deletions(-) diff --git a/docs/sources/setup-grafana/upgrade-grafana.md b/docs/sources/setup-grafana/upgrade-grafana.md index a379fd75a64..40297fc2d7e 100644 --- a/docs/sources/setup-grafana/upgrade-grafana.md +++ b/docs/sources/setup-grafana/upgrade-grafana.md @@ -13,33 +13,33 @@ weight: 500 # Upgrade Grafana -We recommend that you upgrade Grafana often to stay up to date with the latest fixes and enhancements. -In order to make this a reality, Grafana upgrades are backward compatible and the upgrade process is simple and quick. +We recommend that you upgrade Grafana often to stay current with the latest fixes and enhancements. +Because Grafana upgrades are backward compatible, the upgrade process is straightforward. Upgrading between many minor versions and one major version is generally safe, and dashboards and graphs will not change. -Upgrading between many minor versions and one major version is generally safe, and dashboards and graphs will look the same. -There might be minor breaking changes in some releases. -We outline these in the [What's New overviews]({{< relref "../whatsnew/" >}}) for each release. -For versions of Grafana prior to v9.2, we also published additional information in the [Release Notes]({{< relref "../release-notes/" >}}). -We also list all changes, with links to pull requests or issues when available, in the [Changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). +In addition to common tasks you should complete for all versions of Grafana, there might be additional upgrade tasks to complete for a version. -## Backup +> **Note:** There might be minor breaking changes in some releases. We outline these in the [What's New ]({{< relref "../whatsnew/" >}}) document for each release. -We recommend that you backup a few things in case you have to rollback the upgrade. +For versions of Grafana prior to v9.2, we published additional information in the [Release Notes]({{< relref "../release-notes/" >}}). -- Installed plugins - Back them up before you upgrade them in case you want to rollback the Grafana version and want to get the exact same versions you were running before the upgrade. -- Configuration files do not need to be backed up. However, you might want to in case you add new configuration options after upgrade and then rollback. +When available, we list all changes with links to pull requests or issues in the [Changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). -### Database backup +> **Note:** When possible, we recommend that you test the Grafana upgrade process in a test or development environment. -Before upgrading it can be a good idea to backup your Grafana database. This will ensure that you can always rollback to your previous version. During startup, Grafana will automatically migrate the database schema (if there are changes or new tables). Sometimes this can cause issues if you later want to downgrade. +## Back up the Grafana database -#### sqlite +Although Grafana automatically upgrades the database on startup, we recommend that you back up your Grafana database so that you can roll back to a previous version, if required. -If you use sqlite you only need to make a backup of your `grafana.db` file. This is usually located at `/var/lib/grafana/grafana.db` on Unix systems. -If you are unsure what database you use and where it is stored check you grafana configuration file. If you -installed grafana to custom location using a binary tar/zip it is usually in `/data`. +### sqlite -#### mysql +If you use sqlite, you only need to back up the `grafana.db` file. On Unix systems, the database file is usually located in `/var/lib/grafana/`. + +If you are unsure which database you use and where it is stored, check the Grafana configuration file. If you +installed Grafana to a custom location using a binary tar/zip, the database is usually located in `/data`. + +### mysql + +To back up or restore a mysql Grafana database, run the following commands: ```bash backup: @@ -49,7 +49,9 @@ restore: > mysql -u root -p grafana < grafana_backup.sql ``` -#### postgres +### postgres + +To back up or restore a postgres Grafana database, run the following commands: ```bash backup: @@ -59,70 +61,117 @@ restore: > psql grafana < grafana_backup ``` -### Ubuntu or Debian +## Backup plugins -You can upgrade Grafana by following the same procedure as when you installed it. +We recommend that you back up installed plugins before you upgrade Grafana so that you can roll back to a previous version of Grafana, if necessary. -#### Upgrade Debian package +## Upgrade Grafana -If you installed Grafana by downloading a Debian package (`.deb`), then you can execute the same `dpkg -i` command but with the new package. It will upgrade your Grafana installation. +The following sections provide instructions for how to upgrade Grafana based on your installation method. -Go to the [download page](https://grafana.com/grafana/download?platform=linux) for the latest download -links. +### Debian -```bash -wget -sudo apt-get install -y adduser -sudo dpkg -i grafana__amd64.deb -``` +To upgrade Grafana installed from a Debian package (`.deb`), complete the following steps: -#### Upgrade from APT repository +1. In your current installation of Grafana, save your custom configuration changes to a file named `/conf/custom.ini`. -If you installed Grafana from our APT repository, then Grafana will automatically update when you run apt-get upgrade to upgrade all system packages. + This enables you to upgrade Grafana without the risk of losing your configuration changes. -```bash -sudo apt-get update -sudo apt-get upgrade -``` +1. [Download](https://grafana.com/grafana/download?platform=linux) the latest version of Grafana. -#### Upgrade from binary .tar file +1. Execute the `dpkg -i` command. -If you downloaded the binary `.tar.gz` package, then you can just download and extract the new package and overwrite all your existing files. However, this might overwrite your config changes. + ```bash + wget + sudo apt-get install -y adduser + sudo dpkg -i grafana__amd64.deb + ``` -We recommend that you save your custom configuration changes in a file named `/conf/custom.ini`. -This allows you to upgrade Grafana without risking losing your configuration changes. +### APT repository -### Centos / RHEL +To upgrade Grafana installed from the Grafana Labs APT repository, complete the following steps: -If you installed Grafana by downloading an RPM package you can just follow the same installation guide and execute the same `yum install` or `rpm -i` command but with the new package. It will upgrade your Grafana installation. +1. In your current installation of Grafana, save your custom configuration changes to a file named `/conf/custom.ini`. -If you used our YUM repository: + This enables you to upgrade Grafana without the risk of losing your configuration changes. -```bash -sudo yum update grafana -``` +1. Run the following command. + + ```bash + sudo apt-get update + sudo apt-get upgrade + ``` + +Grafana automatically updates when you run `apt-get upgrade`. + +### Binary .tar file + +To upgrade Grafana installed from the binary `.tar.gz` package, complete the following steps: + +1. In your current installation of Grafana, save your custom configuration changes to a file named `/conf/custom.ini`. + + This enables you to upgrade Grafana without the risk of losing your configuration changes. + +1. [Download](https://grafana.com/grafana/download) the binary `.tar.gz` package. + +1. Extract the downloaded package and overwrite the existing files. + +### Centos or RHEL + +To upgrade Grafana running on Centos or RHEL, complete the following steps: + +1. In your current installation of Grafana, save your custom configuration changes to a file named `/conf/custom.ini`. + + This enables you to upgrade Grafana without the risk of losing your configuration changes. + +1. Perform one of the following steps based on your installation. + + - If you [downloaded an RPM package](https://grafana.com/grafana/download) to install Grafana, then complete the steps documented in [Install on RPM-based Linux]({{< relref "./installation/rpm" >}}) to upgrade Grafana. + - If you used the Grafana YUM repository, execute the following command: + + ```bash + sudo yum update grafana + ``` ### Docker -This just an example, details depend on how you configured your grafana container. +To upgrade Grafana running in a Docker container, complete the following steps: -```bash -docker pull grafana/grafana -docker stop my-grafana-container -docker rm my-grafana-container -docker run -d --name=my-grafana-container --restart=always -v /var/lib/grafana:/var/lib/grafana grafana/grafana -``` +1. In your current installation of Grafana, save your custom configuration changes to a file named `/conf/custom.ini`. + + This enables you to upgrade Grafana without the risk of losing your configuration changes. + +1. Run a command similar to the following command. + + > **Note:** This is an example. The parameters you enter depend on how you configured your Grafana container. + + ```bash + docker pull grafana/grafana + docker stop my-grafana-container + docker rm my-grafana-container + docker run -d --name=my-grafana-container --restart=always -v /var/lib/grafana:/var/lib/grafana grafana/grafana + ``` ### Windows -If you downloaded the Windows binary package you can just download a newer package and extract to the same location (and overwrite the existing files). This might overwrite your configuration changes. We recommend that you save your configuration changes in a file named `/conf/custom.ini` as this will make upgrades easier without risking losing your configuration changes. +To upgrade Grafana installed on Windows, complete the following steps: -## Update plugins +1. In your current installation of Grafana, save your custom configuration changes to a file named `/conf/custom.ini`. -After you have upgraded, we strongly recommend that you update all your plugins as a new version of Grafana + This enables you to upgrade Grafana without the risk of losing your configuration changes. + +1. [Download](https://grafana.com/grafana/download) the Windows binary package. + +1. Extract the contents of the package to the location in which you installed Grafana. + + You can overwrite existing files and folders, when prompted. + +## Update Grafana plugins + +After you upgrade Grafana, we recommend that you update all plugins because a new version of Grafana can make older plugins stop working properly. -You can update all plugins using +Run the following command to update plugins: ```bash grafana-cli plugins update-all From 3e73ba54602fb42a18328f1985b1e920fa2e2770 Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Tue, 24 Jan 2023 15:44:48 +0100 Subject: [PATCH 35/46] Azure Monitor: Adapt Advanced component to multiple resources (#61981) --- .../AdvancedResourcePicker.test.tsx | 50 ++++++ .../AdvancedResourcePicker.tsx | 97 +++++++++++ .../LogsQueryEditor/LogsQueryEditor.tsx | 7 + .../AdvancedResourcePicker.test.tsx | 104 +++++++++++ .../AdvancedResourcePicker.tsx | 163 ++++++++++++++++++ .../MetricsQueryEditor/MetricsQueryEditor.tsx | 9 +- .../ResourceField/ResourceField.tsx | 3 + .../ResourcePicker/AdvancedMulti.test.tsx | 16 ++ .../ResourcePicker/AdvancedMulti.tsx | 33 ++++ .../ResourcePicker/ResourcePicker.test.tsx | 97 ++++++++--- .../ResourcePicker/ResourcePicker.tsx | 38 ++-- .../components/ResourcePicker/utils.test.ts | 29 ++++ .../components/ResourcePicker/utils.ts | 10 +- .../e2e/selectors.ts | 3 + 14 files changed, 622 insertions(+), 37 deletions(-) create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.test.tsx create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.tsx create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.test.tsx create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.tsx create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.test.tsx create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.tsx diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.test.tsx new file mode 100644 index 00000000000..91eaa57842d --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.test.tsx @@ -0,0 +1,50 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import AdvancedResourcePicker from './AdvancedResourcePicker'; + +describe('AdvancedResourcePicker', () => { + it('should set a parameter as an object', async () => { + const onChange = jest.fn(); + const { rerender } = render(); + + const subsInput = await screen.findByTestId('input-advanced-resource-picker-1'); + await userEvent.type(subsInput, 'd'); + expect(onChange).toHaveBeenCalledWith(['d']); + + rerender(); + expect(screen.getByDisplayValue('/subscriptions/def-123')).toBeInTheDocument(); + }); + + it('should initialize with an empty resource', () => { + const onChange = jest.fn(); + render(); + expect(onChange).toHaveBeenCalledWith(['']); + }); + + it('should add a resource', async () => { + const onChange = jest.fn(); + render(); + const addButton = await screen.findByText('Add resource URI'); + addButton.click(); + expect(onChange).toHaveBeenCalledWith(['/subscriptions/def-123', '']); + }); + + it('should remove a resource', async () => { + const onChange = jest.fn(); + render(); + const removeButton = await screen.findByTestId('remove-resource'); + removeButton.click(); + expect(onChange).toHaveBeenCalledWith([]); + }); + + it('should render multiple resources', async () => { + render( + + ); + + expect(screen.getByDisplayValue('/subscriptions/def-123')).toBeInTheDocument(); + expect(screen.getByDisplayValue('/subscriptions/def-456')).toBeInTheDocument(); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.tsx new file mode 100644 index 00000000000..fc5d7347507 --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/AdvancedResourcePicker.tsx @@ -0,0 +1,97 @@ +import { css } from '@emotion/css'; +import React, { useEffect } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { AccessoryButton } from '@grafana/experimental'; +import { Icon, Input, Tooltip, Label, Button, useStyles2 } from '@grafana/ui'; + +export interface ResourcePickerProps { + resources: T[]; + onChange: (resources: T[]) => void; +} + +const getStyles = (theme: GrafanaTheme2) => ({ + resourceList: css({ width: '100%', display: 'flex', marginBlock: theme.spacing(1) }), +}); + +const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps) => { + const styles = useStyles2(getStyles); + + useEffect(() => { + // Ensure there is at least one resource + if (resources.length === 0) { + onChange(['']); + } + }, [resources, onChange]); + + const onResourceChange = (index: number, resource: string) => { + const newResources = [...resources]; + newResources[index] = resource; + onChange(newResources); + }; + + const removeResource = (index: number) => { + const newResources = [...resources]; + newResources.splice(index, 1); + onChange(newResources); + }; + + const addResource = () => { + onChange(resources.concat('')); + }; + + return ( + <> + + {resources.map((resource, index) => ( +
+
+ onResourceChange(index, event.currentTarget.value)} + placeholder="ex: /subscriptions/$subId" + data-testid={`input-advanced-resource-picker-${index + 1}`} + /> + removeResource(index)} + data-testid={`remove-resource`} + hidden={resources.length === 1} + /> +
+
+ ))} + + + ); +}; + +export default AdvancedResourcePicker; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx index 9513ee1f6d3..b1c7d95d64c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx @@ -10,6 +10,7 @@ import ResourceField from '../ResourceField'; import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../ResourcePicker/types'; import { parseResourceDetails } from '../ResourcePicker/utils'; +import AdvancedResourcePicker from './AdvancedResourcePicker'; import FormatAsField from './FormatAsField'; import QueryField from './QueryField'; import useMigrations from './useMigrations'; @@ -75,6 +76,12 @@ const LogsQueryEditor: React.FC = ({ resources={query.azureLogAnalytics?.resources ?? []} queryType="logs" disableRow={disableRow} + renderAdvanced={(resources, onChange) => ( + // It's required to cast resources because the resource picker + // specifies the type to string | AzureMetricResource. + // eslint-disable-next-line + + )} /> diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.test.tsx new file mode 100644 index 00000000000..57ff4eaa53a --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.test.tsx @@ -0,0 +1,104 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import AdvancedResourcePicker from './AdvancedResourcePicker'; + +describe('AdvancedResourcePicker', () => { + it('should set a parameter as an object', async () => { + const onChange = jest.fn(); + const { rerender } = render(); + + const subsInput = await screen.findByLabelText('Subscription'); + await userEvent.type(subsInput, 'd'); + expect(onChange).toHaveBeenCalledWith([{ subscription: 'd' }]); + + rerender(); + expect(screen.getByLabelText('Subscription').outerHTML).toMatch('value="def-123"'); + }); + + it('should initialize with an empty resource', () => { + const onChange = jest.fn(); + render(); + expect(onChange).toHaveBeenCalledWith([{}]); + }); + + it('should add a resource', async () => { + const onChange = jest.fn(); + render(); + const addButton = await screen.findByText('Add resource'); + addButton.click(); + expect(onChange).toHaveBeenCalledWith([ + { subscription: 'def-123' }, + { subscription: 'def-123', resourceGroup: '', resourceName: '' }, + ]); + }); + + it('should remove a resource', async () => { + const onChange = jest.fn(); + render(); + const removeButton = await screen.findByTestId('remove-resource'); + removeButton.click(); + expect(onChange).toHaveBeenCalledWith([]); + }); + + it('should update all resources when editing the subscription', async () => { + const onChange = jest.fn(); + render( + + ); + const subsInput = await screen.findByLabelText('Subscription'); + await userEvent.type(subsInput, 'd'); + expect(onChange).toHaveBeenCalledWith([{ subscription: 'def-123d' }, { subscription: 'def-123d' }]); + }); + + it('should update all resources when editing the namespace', async () => { + const onChange = jest.fn(); + render( + + ); + const subsInput = await screen.findByLabelText('Namespace'); + await userEvent.type(subsInput, 'b'); + expect(onChange).toHaveBeenCalledWith([{ metricNamespace: 'aab' }, { metricNamespace: 'aab' }]); + }); + + it('should update all resources when editing the region', async () => { + const onChange = jest.fn(); + render(); + const subsInput = await screen.findByLabelText('Region'); + await userEvent.type(subsInput, 'b'); + expect(onChange).toHaveBeenCalledWith([{ region: 'aab' }, { region: 'aab' }]); + }); + + it('should render multiple resources', async () => { + render( + + ); + + expect(screen.getByDisplayValue('sub1')).toBeInTheDocument(); + expect(screen.getByDisplayValue('ns1')).toBeInTheDocument(); + expect(screen.getByDisplayValue('rg1')).toBeInTheDocument(); + expect(screen.getByDisplayValue('res1')).toBeInTheDocument(); + expect(screen.getByDisplayValue('rg2')).toBeInTheDocument(); + expect(screen.getByDisplayValue('res2')).toBeInTheDocument(); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.tsx new file mode 100644 index 00000000000..94bc7c88823 --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AdvancedResourcePicker.tsx @@ -0,0 +1,163 @@ +import { css } from '@emotion/css'; +import React, { useEffect } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { AccessoryButton } from '@grafana/experimental'; +import { Input, Label, InlineField, Button, useStyles2 } from '@grafana/ui'; + +import { selectors } from '../../e2e/selectors'; +import { AzureMetricResource } from '../../types'; + +export interface ResourcePickerProps { + resources: T[]; + onChange: (resources: T[]) => void; +} + +const getStyles = (theme: GrafanaTheme2) => ({ + resourceList: css({ display: 'flex', columnGap: theme.spacing(1), flexWrap: 'wrap', marginBottom: theme.spacing(1) }), + resource: css({ flex: '0 0 auto' }), + resourceLabel: css({ padding: theme.spacing(1) }), + resourceGroupAndName: css({ display: 'flex', columnGap: theme.spacing(0.5) }), +}); + +const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps) => { + const styles = useStyles2(getStyles); + + useEffect(() => { + // Ensure there is at least one resource + if (resources.length === 0) { + onChange([{}]); + } + }, [resources, onChange]); + + const onResourceChange = (index: number, resource: AzureMetricResource) => { + const newResources = [...resources]; + newResources[index] = resource; + onChange(newResources); + }; + + const removeResource = (index: number) => { + const newResources = [...resources]; + newResources.splice(index, 1); + onChange(newResources); + }; + + const addResource = () => { + onChange( + resources.concat({ + subscription: resources[0]?.subscription, + metricNamespace: resources[0]?.metricNamespace, + resourceGroup: '', + resourceName: '', + }) + ); + }; + + const onCommonPropChange = (r: Partial) => { + onChange(resources.map((resource) => ({ ...resource, ...r }))); + }; + + return ( + <> + + onCommonPropChange({ subscription: event.currentTarget.value })} + placeholder="aaaaaaaa-bbbb-cccc-dddd-eeeeeeee" + /> + + + onCommonPropChange({ metricNamespace: event.currentTarget.value })} + placeholder="Microsoft.Insights/metricNamespaces" + /> + + + onCommonPropChange({ region: event.currentTarget.value })} + placeholder="northeurope" + /> + +
+ {resources.map((resource, index) => ( +
+ {resources.length !== 1 && } + +
+ + onResourceChange(index, { ...resource, resourceGroup: event.currentTarget.value }) + } + placeholder="resource-group" + /> + removeResource(index)} + hidden={resources.length === 1} + data-testid={'remove-resource'} + /> +
+
+ + + onResourceChange(index, { ...resource, resourceName: event.currentTarget.value })} + placeholder="name" + /> + +
+ ))} +
+ + + ); +}; + +export default AdvancedResourcePicker; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx index 262840b382d..ec2e079fbb5 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx @@ -6,11 +6,12 @@ import { config } from '@grafana/runtime'; import { multiResourceCompatibleTypes } from '../../azureMetadata'; import type Datasource from '../../datasource'; -import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types'; +import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish, AzureMetricResource } from '../../types'; import ResourceField from '../ResourceField'; import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../ResourcePicker/types'; import { parseResourceDetails } from '../ResourcePicker/utils'; +import AdvancedResourcePicker from './AdvancedResourcePicker'; import AggregationField from './AggregationField'; import DimensionFields from './DimensionFields'; import LegendFormatField from './LegendFormatField'; @@ -88,6 +89,12 @@ const MetricsQueryEditor: React.FC = ({ resources={resources ?? []} queryType={'metrics'} disableRow={disableRow} + renderAdvanced={(resources, onChange) => ( + // It's required to cast resources because the resource picker + // specifies the type to string | AzureMetricResource. + // eslint-disable-next-line + + )} /> extends AzureQueryEditorFieldProps { inlineField?: boolean; labelWidth?: number; disableRow: (row: ResourceRow, selectedRows: ResourceRowGroup) => boolean; + renderAdvanced: (resources: T[], onChange: (resources: T[]) => void) => React.ReactNode; } const ResourceField: React.FC> = ({ @@ -32,6 +33,7 @@ const ResourceField: React.FC> inlineField, labelWidth, disableRow, + renderAdvanced, }) => { const styles = useStyles2(getStyles); const [pickerIsOpen, setPickerIsOpen] = useState(false); @@ -71,6 +73,7 @@ const ResourceField: React.FC> selectableEntryTypes={selectableEntryTypes} queryType={queryType} disableRow={disableRow} + renderAdvanced={renderAdvanced} /> diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.test.tsx new file mode 100644 index 00000000000..d6a5d9e2cce --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.test.tsx @@ -0,0 +1,16 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import AdvancedMulti from './AdvancedMulti'; + +describe('AdvancedMulti', () => { + it('should expand and render a section', async () => { + const onChange = jest.fn(); + const renderAdvanced = jest.fn().mockReturnValue(
details!
); + render(); + const advancedSection = screen.getByText('Advanced'); + advancedSection.click(); + + expect(await screen.findByText('details!')).toBeInTheDocument(); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.tsx new file mode 100644 index 00000000000..9c7b9b35e51 --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/AdvancedMulti.tsx @@ -0,0 +1,33 @@ +import React, { useState } from 'react'; + +import { Collapse } from '@grafana/ui'; + +import { selectors } from '../../e2e/selectors'; +import { AzureMetricResource } from '../../types'; +import { Space } from '../Space'; + +export interface ResourcePickerProps { + resources: T[]; + onChange: (resources: T[]) => void; + renderAdvanced: (resources: T[], onChange: (resources: T[]) => void) => React.ReactNode; +} + +const AdvancedMulti = ({ resources, onChange, renderAdvanced }: ResourcePickerProps) => { + const [isAdvancedOpen, setIsAdvancedOpen] = useState(!!resources.length && JSON.stringify(resources).includes('$')); + + return ( +
+ setIsAdvancedOpen(!isAdvancedOpen)} + > + {renderAdvanced(resources, onChange)} + + +
+ ); +}; + +export default AdvancedMulti; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx index 0c201f583ab..3bad6927b27 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx @@ -59,7 +59,7 @@ const queryType: ResourcePickerQueryType = 'logs'; const defaultProps = { templateVariables: [], - resources: [noResourceURI], + resources: [], resourcePickerData: createMockResourcePickerData(), onCancel: noop, onApply: noop, @@ -71,6 +71,7 @@ const defaultProps = { ], queryType, disableRow: jest.fn(), + renderAdvanced: jest.fn(), }; describe('AzureMonitor ResourcePicker', () => { @@ -141,6 +142,7 @@ describe('AzureMonitor ResourcePicker', () => { expect(subscriptionCheckbox).not.toBeChecked(); subscriptionCheckbox.click(); const applyButton = screen.getByRole('button', { name: 'Apply' }); + expect(applyButton).toBeEnabled(); applyButton.click(); expect(onApply).toBeCalledTimes(1); expect(onApply).toBeCalledWith(['/subscriptions/def-123']); @@ -174,26 +176,56 @@ describe('AzureMonitor ResourcePicker', () => { expect(onApply).toBeCalledWith([]); }); - it('should call onApply with a new subscription when a user clicks on the checkbox in the row', async () => { + it('should call onApply with a new resource when a user clicks on the checkbox in the row', async () => { const onApply = jest.fn(); - render(); - const subscriptionCheckbox = await screen.findByLabelText('Primary Subscription'); - expect(subscriptionCheckbox).toBeInTheDocument(); - expect(subscriptionCheckbox).not.toBeChecked(); - subscriptionCheckbox.click(); + render(); + + const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' }); + expect(subscriptionButton).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Expand A Great Resource Group' })).not.toBeInTheDocument(); + subscriptionButton.click(); + + const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' }); + resourceGroupButton.click(); + const checkbox = await screen.findByLabelText('web-server'); + await userEvent.click(checkbox); + expect(checkbox).toBeChecked(); const applyButton = screen.getByRole('button', { name: 'Apply' }); applyButton.click(); + expect(onApply).toBeCalledTimes(1); - expect(onApply).toBeCalledWith([{ subscription: 'def-123' }]); + expect(onApply).toBeCalledWith([ + { + metricNamespace: 'Microsoft.Compute/virtualMachines', + region: 'northeurope', + resourceGroup: 'dev-3', + resourceName: 'web-server', + subscription: 'def-456', + }, + ]); }); it('should call onApply removing a resource element', async () => { const onApply = jest.fn(); - render(); - const subscriptionCheckbox = await screen.findAllByLabelText('Primary Subscription'); - expect(subscriptionCheckbox).toHaveLength(2); - expect(subscriptionCheckbox.at(0)).toBeChecked(); - subscriptionCheckbox.at(0)?.click(); + render( + + ); + const checkbox = await screen.findAllByLabelText('web-server'); + expect(checkbox).toHaveLength(2); + expect(checkbox.at(0)).toBeChecked(); + checkbox.at(0)?.click(); const applyButton = screen.getByRole('button', { name: 'Apply' }); applyButton.click(); expect(onApply).toBeCalledTimes(1); @@ -202,7 +234,7 @@ describe('AzureMonitor ResourcePicker', () => { it('should call onApply with a new subscription uri when a user types it in the selection box', async () => { const onApply = jest.fn(); - render(); + render(); const subscriptionCheckbox = await screen.findByLabelText('Primary Subscription'); expect(subscriptionCheckbox).toBeInTheDocument(); expect(subscriptionCheckbox).not.toBeChecked(); @@ -222,7 +254,7 @@ describe('AzureMonitor ResourcePicker', () => { it('should call onApply with a new subscription when a user types it in the selection box', async () => { const onApply = jest.fn(); - render(); + render(); const subscriptionCheckbox = await screen.findByLabelText('Primary Subscription'); expect(subscriptionCheckbox).toBeInTheDocument(); expect(subscriptionCheckbox).not.toBeChecked(); @@ -232,20 +264,41 @@ describe('AzureMonitor ResourcePicker', () => { const advancedInput = await screen.findByLabelText('Subscription'); await userEvent.type(advancedInput, 'def-123'); + const nsInput = await screen.findByLabelText('Namespace'); + await userEvent.type(nsInput, 'ns'); + const rgInput = await screen.findByLabelText('Resource Group'); + await userEvent.type(rgInput, 'rg'); + const rnInput = await screen.findByLabelText('Resource Name'); + await userEvent.type(rnInput, 'rn'); const applyButton = screen.getByRole('button', { name: 'Apply' }); applyButton.click(); expect(onApply).toBeCalledTimes(1); - expect(onApply).toBeCalledWith([{ subscription: 'def-123' }]); + expect(onApply).toBeCalledWith([ + { subscription: 'def-123', metricNamespace: 'ns', resourceGroup: 'rg', resourceName: 'rn' }, + ]); }); it('should show unselect a subscription if the value is manually edited', async () => { - render(); - const subscriptionCheckboxes = await screen.findAllByLabelText('Dev Subscription'); - expect(subscriptionCheckboxes.length).toBe(2); - expect(subscriptionCheckboxes[0]).toBeChecked(); - expect(subscriptionCheckboxes[1]).toBeChecked(); + render( + + ); + const checkboxes = await screen.findAllByLabelText('web-server'); + expect(checkboxes.length).toBe(2); + expect(checkboxes[0]).toBeChecked(); + expect(checkboxes[1]).toBeChecked(); const advancedSection = screen.getByText('Advanced'); advancedSection.click(); @@ -253,7 +306,7 @@ describe('AzureMonitor ResourcePicker', () => { const advancedInput = await screen.findByLabelText('Subscription'); await userEvent.type(advancedInput, 'def-123'); - const updatedCheckboxes = await screen.findAllByLabelText('Dev Subscription'); + const updatedCheckboxes = await screen.findAllByLabelText('web-server'); expect(updatedCheckboxes.length).toBe(1); expect(updatedCheckboxes[0]).not.toBeChecked(); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.tsx index 6eb36f5776f..c99550162c3 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.tsx @@ -2,6 +2,7 @@ import { cx } from '@emotion/css'; import React, { useCallback, useEffect, useState } from 'react'; import { useEffectOnce } from 'react-use'; +import { config } from '@grafana/runtime'; import { Alert, Button, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -11,6 +12,7 @@ import messageFromError from '../../utils/messageFromError'; import { Space } from '../Space'; import Advanced from './Advanced'; +import AdvancedMulti from './AdvancedMulti'; import NestedRow from './NestedRow'; import Search from './Search'; import getStyles from './styles'; @@ -26,6 +28,7 @@ interface ResourcePickerProps { onApply: (resources: T[]) => void; onCancel: () => void; disableRow: (row: ResourceRow, selectedRows: ResourceRowGroup) => boolean; + renderAdvanced: (resources: T[], onChange: (resources: T[]) => void) => React.ReactNode; } const ResourcePicker = ({ @@ -36,6 +39,7 @@ const ResourcePicker = ({ selectableEntryTypes, queryType, disableRow, + renderAdvanced, }: ResourcePickerProps) => { const styles = useStyles2(getStyles); @@ -71,13 +75,18 @@ const ResourcePicker = ({ loadInitialData(); }); + // Avoid using empty resources + const isValid = (r: string | AzureMetricResource) => + typeof r === 'string' ? r !== '' : r.subscription && r.resourceGroup && r.resourceName && r.metricNamespace; + // set selected row data whenever row or selection changes useEffect(() => { if (!internalSelected) { setSelectedRows([]); } - const found = internalSelected && findRows(rows, resourcesToStrings(internalSelected)); + const sanitized = internalSelected.filter((r) => isValid(r)); + const found = internalSelected && findRows(rows, resourcesToStrings(sanitized)); if (found && found.length) { return setSelectedRows(found); } @@ -106,15 +115,11 @@ const ResourcePicker = ({ [resourcePickerData, rows, queryType] ); - const resourceIsString = resources?.length && typeof resources[0] === 'string'; const handleSelectionChanged = useCallback( (row: ResourceRow, isSelected: boolean) => { if (isSelected) { - const newRes = resourceIsString ? row.uri : parseMultipleResourceDetails([row.uri], row.location)[0]; - const newSelected = (internalSelected ? internalSelected.concat(newRes) : [newRes]).filter((r) => { - // avoid setting empty resources - return typeof r === 'string' ? r !== '' : r.subscription; - }); + const newRes = queryType === 'logs' ? row.uri : parseMultipleResourceDetails([row.uri], row.location)[0]; + const newSelected = internalSelected ? internalSelected.concat(newRes) : [newRes]; setInternalSelected(newSelected); } else { const newInternalSelected = internalSelected?.filter((r) => { @@ -123,14 +128,14 @@ const ResourcePicker = ({ setInternalSelected(newInternalSelected); } }, - [resourceIsString, internalSelected, setInternalSelected] + [queryType, internalSelected, setInternalSelected] ); const handleApply = useCallback(() => { if (internalSelected) { - onApply(resourceIsString ? internalSelected : parseMultipleResourceDetails(internalSelected)); + onApply(queryType === 'logs' ? internalSelected : parseMultipleResourceDetails(internalSelected)); } - }, [resourceIsString, internalSelected, onApply]); + }, [queryType, internalSelected, onApply]); const handleSearch = useCallback( async (searchWord: string) => { @@ -239,11 +244,20 @@ const ResourcePicker = ({ )} - setInternalSelected(r)} /> + {config.featureToggles.azureMultipleResourcePicker ? ( + setInternalSelected(r)} + renderAdvanced={renderAdvanced} + /> + ) : ( + setInternalSelected(r)} /> + )} + - ) : ( - message - )} - - ); -} - function createVisualisationData( logLinesBased: DataQueryResponse | undefined, logLinesBasedVisibleRange: AbsoluteTimeRange | undefined, @@ -110,7 +81,7 @@ export function LogsVolumePanel(props: Props) { const { logsVolumeData, fullRangeData, range } = data; if (logsVolumeData.error !== undefined) { - return ; + return ; } let LogsVolumePanelContent; diff --git a/public/app/features/explore/SupplementaryResultError.test.tsx b/public/app/features/explore/SupplementaryResultError.test.tsx new file mode 100644 index 00000000000..c735d1b9818 --- /dev/null +++ b/public/app/features/explore/SupplementaryResultError.test.tsx @@ -0,0 +1,30 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { SupplementaryResultError } from './SupplementaryResultError'; + +describe('SupplementaryResultError', () => { + it('shows short warning message', () => { + const error = { data: { message: 'Test error message' } }; + const title = 'Error loading supplementary query'; + + render(); + expect(screen.getByText(title)).toBeInTheDocument(); + expect(screen.getByText(error.data.message)).toBeInTheDocument(); + }); + + it('shows long warning message', () => { + // we make a long message + const messagePart = 'One two three four five six seven eight nine ten.'; + const message = messagePart.repeat(3); + const error = { data: { message } }; + const title = 'Error loading supplementary query'; + + render(); + expect(screen.getByText(title)).toBeInTheDocument(); + expect(screen.queryByText(message)).not.toBeInTheDocument(); + const button = screen.getByText('Show details'); + button.click(); + expect(screen.getByText(message)).toBeInTheDocument(); + }); +}); diff --git a/public/app/features/explore/SupplementaryResultError.tsx b/public/app/features/explore/SupplementaryResultError.tsx new file mode 100644 index 00000000000..237c2072db5 --- /dev/null +++ b/public/app/features/explore/SupplementaryResultError.tsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; + +import { DataQueryError } from '@grafana/data'; +import { Alert, Button } from '@grafana/ui'; + +type Props = { + error: DataQueryError; + title: string; +}; +export function SupplementaryResultError(props: Props) { + const [isOpen, setIsOpen] = useState(false); + const SHORT_ERROR_MESSAGE_LIMIT = 100; + const { error, title } = props; + // generic get-error-message-logic, taken from + // /public/app/features/explore/ErrorContainer.tsx + const message = error.message || error.data?.message || ''; + const showButton = !isOpen && message.length > SHORT_ERROR_MESSAGE_LIMIT; + + return ( + + {showButton ? ( + + ) : ( + message + )} + + ); +} diff --git a/public/app/features/explore/state/query.test.ts b/public/app/features/explore/state/query.test.ts index 5e039079985..bfb5223a52b 100644 --- a/public/app/features/explore/state/query.test.ts +++ b/public/app/features/explore/state/query.test.ts @@ -517,7 +517,7 @@ describe('reducer', () => { mockDataProvider = () => { return of({ state: LoadingState.Done, error: undefined, data: [{}] }); }; - // turn logs volume off (but keep log sample on) + // turn logs volume off (but keep logs sample on) dispatch(setSupplementaryQueryEnabled(ExploreId.left, false, SupplementaryQueryType.LogsVolume)); expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe( false diff --git a/public/app/features/explore/utils/supplementaryQueries.ts b/public/app/features/explore/utils/supplementaryQueries.ts index 3847d6775f1..cef393d2401 100644 --- a/public/app/features/explore/utils/supplementaryQueries.ts +++ b/public/app/features/explore/utils/supplementaryQueries.ts @@ -17,8 +17,7 @@ export const loadSupplementaryQueries = (): SupplementaryQueries => { // We default to true for all supp queries let supplementaryQueries: SupplementaryQueries = { [SupplementaryQueryType.LogsVolume]: { enabled: true }, - // This is set to false temporarily, until we have UI to display logs sample and a way how to enable/disable it - [SupplementaryQueryType.LogsSample]: { enabled: false }, + [SupplementaryQueryType.LogsSample]: { enabled: true }, }; for (const type of supplementaryQueryTypes) { diff --git a/public/app/features/inspector/InspectDataTab.tsx b/public/app/features/inspector/InspectDataTab.tsx index df9239a76fa..b615a008614 100644 --- a/public/app/features/inspector/InspectDataTab.tsx +++ b/public/app/features/inspector/InspectDataTab.tsx @@ -108,7 +108,7 @@ export class InspectDataTab extends PureComponent { area: 'inspector', }); - const logsModel = dataFrameToLogsModel(data || [], undefined); + const logsModel = dataFrameToLogsModel(data || []); downloadLogsModelAsTxt(logsModel, panel ? panel.getDisplayTitle() : 'Explore'); }; diff --git a/public/app/features/logs/components/LogRowMessage.tsx b/public/app/features/logs/components/LogRowMessage.tsx index 37dffc7228b..e0fd1d69fb7 100644 --- a/public/app/features/logs/components/LogRowMessage.tsx +++ b/public/app/features/logs/components/LogRowMessage.tsx @@ -32,7 +32,7 @@ interface Props extends Themeable2 { logsSortOrder?: LogsSortOrder | null; } -const getStyles = (theme: GrafanaTheme2, showContextButton: boolean, isInDashboard: boolean | undefined) => { +const getStyles = (theme: GrafanaTheme2, showContextButton: boolean, isInExplore: boolean) => { const outlineColor = tinycolor(theme.components.dashboard.background).setAlpha(0.7).toRgbString(); return { @@ -74,7 +74,7 @@ const getStyles = (theme: GrafanaTheme2, showContextButton: boolean, isInDashboa `, logRowMenuCell: css` position: absolute; - right: ${isInDashboard ? '40px' : `calc(75px + ${theme.spacing()} + ${showContextButton ? '80px' : '40px'})`}; + right: ${!isInExplore ? '40px' : `calc(75px + ${theme.spacing()} + ${showContextButton ? '80px' : '40px'})`}; margin-top: -${theme.spacing(0.125)}; `, logLine: css` @@ -169,7 +169,7 @@ class UnThemedLogRowMessage extends PureComponent { const { hasAnsi, raw } = row; const restructuredEntry = restructureLog(raw, prettifyLogMessage); const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false; - const styles = getStyles(theme, shouldShowContextToggle, app === CoreApp.Dashboard); + const styles = getStyles(theme, shouldShowContextToggle, app === CoreApp.Explore); return ( <> diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 7713d39b36b..a88ef2a63d1 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -471,7 +471,7 @@ export class LokiDatasource } async getDataSamples(query: LokiQuery): Promise { - // Currently works only for log samples + // Currently works only for logs sample if (!isValidQuery(query.expr) || !isLogsQuery(query.expr)) { return []; }