From 4274b9414fdd9a603dc284011822ff78b597566e Mon Sep 17 00:00:00 2001 From: Artur Wierzbicki Date: Mon, 20 Mar 2023 20:00:14 +0400 Subject: [PATCH] Chore: remove `querylibrary` feature toggle (#65021) * chore: remove querylibrary * chore: remove querylibrary * chore: remove querylibrary --- .betterer.results | 7 - .github/CODEOWNERS | 2 - .../feature-toggles/index.md | 1 - .../src/types/featureToggles.gen.ts | 1 - pkg/api/api.go | 4 - pkg/api/dashboard.go | 6 - pkg/api/http_server.go | 8 +- .../manager/manager_integration_test.go | 2 +- pkg/server/wire.go | 3 - pkg/services/featuremgmt/registry.go | 7 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/navtree/models.go | 1 - pkg/services/navtree/navtreeimpl/navtree.go | 17 +- .../querylibrary/querylibraryimpl/http.go | 87 ----- .../querylibrary/querylibraryimpl/service.go | 298 ----------------- pkg/services/querylibrary/tests/api_client.go | 286 ----------------- pkg/services/querylibrary/tests/common.go | 74 ----- .../tests/querylibrary_integration_test.go | 299 ------------------ pkg/services/querylibrary/types.go | 88 ------ pkg/services/searchV2/allowed_actions_test.go | 2 +- pkg/services/searchV2/queries.go | 113 ------- pkg/services/searchV2/service.go | 9 +- pkg/services/searchV2/service_bench_test.go | 4 +- .../PanelEditor/PanelEditorQueries.tsx | 1 - .../features/dashboard/state/PanelModel.ts | 15 - .../query-library/api/SavedQueriesApi.ts | 72 ----- .../query-library/api/SavedQueriesSrv.ts | 26 -- .../components/CreateNewQuery.tsx | 123 ------- .../components/DatasourceTypePicker.tsx | 113 ------- .../query-library/components/HistoryTab.tsx | 27 -- .../query-library/components/Queries.tsx | 25 -- .../components/QueryCreateDrawer.tsx | 118 ------- .../query-library/components/QueryEditor.tsx | 143 --------- .../components/QueryEditorDrawer.tsx | 108 ------- .../components/QueryEditorDrawerHeader.tsx | 198 ------------ .../components/QueryImportDrawer.tsx | 55 ---- .../components/QueryLibraryPage.tsx | 46 --- .../components/QueryLibrarySearchTable.tsx | 219 ------------- .../components/QueryListItem.tsx | 198 ------------ .../query-library/components/QueryName.tsx | 119 ------- .../components/SaveQueryWorkflowModal.tsx | 79 ----- .../query-library/components/UsagesTab.tsx | 166 ---------- .../query-library/components/VariablesTab.tsx | 166 ---------- .../features/query-library/globalStyles.ts | 28 -- .../query-library/img/grafana_incident.svg | 10 - .../features/query-library/img/grafana_ml.svg | 1 - .../query-library/img/grafana_oncall.svg | 9 - public/app/features/query-library/routes.tsx | 20 -- public/app/features/query-library/types.ts | 30 -- public/app/features/query-library/utils.ts | 14 - .../features/query/components/QueryGroup.tsx | 78 ----- public/app/routes/routes.tsx | 2 - 53 files changed, 6 insertions(+), 3527 deletions(-) delete mode 100644 pkg/services/querylibrary/querylibraryimpl/http.go delete mode 100644 pkg/services/querylibrary/querylibraryimpl/service.go delete mode 100644 pkg/services/querylibrary/tests/api_client.go delete mode 100644 pkg/services/querylibrary/tests/common.go delete mode 100644 pkg/services/querylibrary/tests/querylibrary_integration_test.go delete mode 100644 pkg/services/querylibrary/types.go delete mode 100644 pkg/services/searchV2/queries.go delete mode 100644 public/app/features/query-library/api/SavedQueriesApi.ts delete mode 100644 public/app/features/query-library/api/SavedQueriesSrv.ts delete mode 100644 public/app/features/query-library/components/CreateNewQuery.tsx delete mode 100644 public/app/features/query-library/components/DatasourceTypePicker.tsx delete mode 100644 public/app/features/query-library/components/HistoryTab.tsx delete mode 100644 public/app/features/query-library/components/Queries.tsx delete mode 100644 public/app/features/query-library/components/QueryCreateDrawer.tsx delete mode 100644 public/app/features/query-library/components/QueryEditor.tsx delete mode 100644 public/app/features/query-library/components/QueryEditorDrawer.tsx delete mode 100644 public/app/features/query-library/components/QueryEditorDrawerHeader.tsx delete mode 100644 public/app/features/query-library/components/QueryImportDrawer.tsx delete mode 100644 public/app/features/query-library/components/QueryLibraryPage.tsx delete mode 100644 public/app/features/query-library/components/QueryLibrarySearchTable.tsx delete mode 100644 public/app/features/query-library/components/QueryListItem.tsx delete mode 100644 public/app/features/query-library/components/QueryName.tsx delete mode 100644 public/app/features/query-library/components/SaveQueryWorkflowModal.tsx delete mode 100644 public/app/features/query-library/components/UsagesTab.tsx delete mode 100644 public/app/features/query-library/components/VariablesTab.tsx delete mode 100644 public/app/features/query-library/globalStyles.ts delete mode 100644 public/app/features/query-library/img/grafana_incident.svg delete mode 100644 public/app/features/query-library/img/grafana_ml.svg delete mode 100644 public/app/features/query-library/img/grafana_oncall.svg delete mode 100644 public/app/features/query-library/routes.tsx delete mode 100644 public/app/features/query-library/types.ts delete mode 100644 public/app/features/query-library/utils.ts diff --git a/.betterer.results b/.betterer.results index 26e915da394..87a05c981f9 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3751,13 +3751,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Do not use any type assertions.", "3"] ], - "public/app/features/query-library/components/DatasourceTypePicker.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"] - ], - "public/app/features/query-library/components/SaveQueryWorkflowModal.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] - ], "public/app/features/query/components/QueryEditorRow.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9b537f631e2..adaf11a29dd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -255,7 +255,6 @@ /pkg/services/live/ @grafana/grafana-app-platform-squad /pkg/services/searchV2/ @grafana/grafana-app-platform-squad /pkg/services/store/ @grafana/grafana-app-platform-squad -/pkg/services/querylibrary/ @grafana/grafana-app-platform-squad /pkg/infra/filestorage/ @grafana/grafana-app-platform-squad /pkg/util/converter/ @grafana/grafana-app-platform-squad /pkg/modules/ @grafana/grafana-app-platform-squad @@ -373,7 +372,6 @@ lerna.json @grafana/frontend-ops /public/app/features/profile/ @grafana/user-essentials /public/app/features/runtime/ @ryantxu /public/app/features/query/ @grafana/dashboards-squad -/public/app/features/query-library/ @grafana/dataviz-squad /public/app/features/sandbox/ @grafana/user-essentials /public/app/features/scenes/ @grafana/dashboards-squad /public/app/features/search/ @grafana/user-essentials 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 446288dd46f..99ec4435a1f 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -106,5 +106,4 @@ The following toggles require explicitly setting Grafana's [app mode]({{< relref | `dashboardsFromStorage` | Load dashboards from the generic storage interface | | `grpcServer` | Run GRPC server | | `entityStore` | SQL-based entity store (requires storage flag also) | -| `queryLibrary` | Reusable query library | | `nestedFolders` | Enable folder nesting | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 19dc07f7488..69e65a27c48 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -61,7 +61,6 @@ export interface FeatureToggles { redshiftAsyncQueryDataSupport?: boolean; athenaAsyncQueryDataSupport?: boolean; newPanelChromeUI?: boolean; - queryLibrary?: boolean; showDashboardValidationWarnings?: boolean; mysqlAnsiQuotes?: boolean; accessControlOnCall?: boolean; diff --git a/pkg/api/api.go b/pkg/api/api.go index 76f8cc4223d..3e1343731a9 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -309,10 +309,6 @@ func (hs *HTTPServer) registerRoutes() { apiRoute.Group("/search-v2", hs.SearchV2HTTPService.RegisterHTTPRoutes) } - if hs.QueryLibraryHTTPService != nil && !hs.QueryLibraryHTTPService.IsDisabled() { - apiRoute.Group("/query-library", hs.QueryLibraryHTTPService.RegisterHTTPRoutes) - } - // current org apiRoute.Group("/org", func(orgRoute routing.RouteRegister) { userIDScope := ac.Scope("users", "id", ac.Parameter(":userId")) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 09a9344b122..e10aeeaceb1 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -227,12 +227,6 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response // make sure db version is in sync with json model version dash.Data.Set("version", dash.Version) - if hs.QueryLibraryService != nil && !hs.QueryLibraryService.IsDisabled() { - if err := hs.QueryLibraryService.UpdateDashboardQueries(c.Req.Context(), c.SignedInUser, dash); err != nil { - return response.Error(500, "Error while loading saved queries", err) - } - } - dto := dtos.DashboardFullWithMeta{ Dashboard: dash.Data, Meta: meta, diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index ebbf0522fc4..11927f139ad 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -74,7 +74,6 @@ 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" @@ -147,8 +146,6 @@ type HTTPServer struct { StorageService store.StorageService httpEntityStore httpentitystore.HTTPEntityStore SearchV2HTTPService searchV2.SearchHTTPService - QueryLibraryHTTPService querylibrary.HTTPService - QueryLibraryService querylibrary.Service ContextHandler *contexthandler.ContextHandler SQLStore db.DB AlertEngine *alerting.AlertEngine @@ -250,8 +247,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service, loginAttemptService loginAttempt.Service, orgService org.Service, teamService team.Service, accesscontrolService accesscontrol.Service, dashboardThumbsService thumbs.DashboardThumbService, navTreeService navtree.Service, - annotationRepo annotations.Repository, tagService tag.Service, searchv2HTTPService searchV2.SearchHTTPService, - queryLibraryHTTPService querylibrary.HTTPService, queryLibraryService querylibrary.Service, oauthTokenService oauthtoken.OAuthTokenService, + annotationRepo annotations.Repository, tagService tag.Service, searchv2HTTPService searchV2.SearchHTTPService, oauthTokenService oauthtoken.OAuthTokenService, statsService stats.Service, authnService authn.Service, pluginsCDNService *pluginscdn.Service, starApi *starApi.API, ) (*HTTPServer, error) { @@ -352,8 +348,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi accesscontrolService: accesscontrolService, annotationsRepo: annotationRepo, tagService: tagService, - QueryLibraryHTTPService: queryLibraryHTTPService, - QueryLibraryService: queryLibraryService, oauthTokenService: oauthTokenService, statsService: statsService, authnService: authnService, diff --git a/pkg/plugins/manager/manager_integration_test.go b/pkg/plugins/manager/manager_integration_test.go index f33e0deb596..945a057e815 100644 --- a/pkg/plugins/manager/manager_integration_test.go +++ b/pkg/plugins/manager/manager_integration_test.go @@ -104,7 +104,7 @@ func TestIntegrationPluginManager(t *testing.T) { pg := postgres.ProvideService(cfg) my := mysql.ProvideService(cfg, hcp) ms := mssql.ProvideService(cfg) - sv2 := searchV2.ProvideService(cfg, db.InitTestDB(t), nil, nil, tracer, features, nil, nil, nil, nil) + sv2 := searchV2.ProvideService(cfg, db.InitTestDB(t), nil, nil, tracer, features, nil, nil, nil) graf := grafanads.ProvideService(sv2, nil) phlare := phlare.ProvideService(hcp) parca := parca.ProvideService(hcp) diff --git a/pkg/server/wire.go b/pkg/server/wire.go index df64dbba62c..d266c0776ef 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -97,7 +97,6 @@ import ( publicdashboardsService "github.com/grafana/grafana/pkg/services/publicdashboards/service" "github.com/grafana/grafana/pkg/services/query" "github.com/grafana/grafana/pkg/services/queryhistory" - "github.com/grafana/grafana/pkg/services/querylibrary/querylibraryimpl" "github.com/grafana/grafana/pkg/services/quota/quotaimpl" "github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/search" @@ -263,8 +262,6 @@ var wireBasicSet = wire.NewSet( secretsManager.ProvideSecretsService, wire.Bind(new(secrets.Service), new(*secretsManager.SecretsService)), secretsDatabase.ProvideSecretsStore, - querylibraryimpl.ProvideService, - querylibraryimpl.ProvideHTTPService, wire.Bind(new(secrets.Store), new(*secretsDatabase.SecretsStoreImpl)), secretsMigrator.ProvideSecretsMigrator, wire.Bind(new(secrets.Migrator), new(*secretsMigrator.SecretsMigrator)), diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index ed0e63fac1a..5f4a9189b4e 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -289,13 +289,6 @@ var ( FrontendOnly: true, Owner: grafanaDashboardsSquad, }, - { - Name: "queryLibrary", - Description: "Reusable query library", - State: FeatureStateAlpha, - RequiresDevMode: true, - Owner: grafanaAppPlatformSquad, - }, { Name: "showDashboardValidationWarnings", Description: "Show warnings when dashboards do not validate against the schema", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 859f1682f0d..5032651915a 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -42,7 +42,6 @@ cloudWatchCrossAccountQuerying,stable,@grafana/aws-plugins,false,false,false,fal redshiftAsyncQueryDataSupport,alpha,@grafana/aws-plugins,false,false,false,true athenaAsyncQueryDataSupport,alpha,@grafana/aws-plugins,false,false,false,true newPanelChromeUI,alpha,@grafana/dashboards-squad,false,false,false,true -queryLibrary,alpha,@grafana/grafana-app-platform-squad,true,false,false,false showDashboardValidationWarnings,alpha,@grafana/dashboards-squad,false,false,false,false mysqlAnsiQuotes,alpha,@grafana/backend-platform,false,false,false,false accessControlOnCall,beta,@grafana/grafana-authnz-team,false,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 032ec763204..fdd3d16d1d8 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -179,10 +179,6 @@ const ( // Show updated look and feel of grafana-ui PanelChrome: panel header, icons, and menu FlagNewPanelChromeUI = "newPanelChromeUI" - // FlagQueryLibrary - // Reusable query library - FlagQueryLibrary = "queryLibrary" - // FlagShowDashboardValidationWarnings // Show warnings when dashboards do not validate against the schema FlagShowDashboardValidationWarnings = "showDashboardValidationWarnings" diff --git a/pkg/services/navtree/models.go b/pkg/services/navtree/models.go index 3be070331be..e857c08d848 100644 --- a/pkg/services/navtree/models.go +++ b/pkg/services/navtree/models.go @@ -15,7 +15,6 @@ const ( WeightSavedItems WeightCreate WeightDashboard - WeightQueryLibrary WeightExplore WeightAlerting WeightDataConnections diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 66ae88f7a11..aa9a5ea018c 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -18,7 +18,6 @@ import ( "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" pref "github.com/grafana/grafana/pkg/services/preference" - "github.com/grafana/grafana/pkg/services/querylibrary" "github.com/grafana/grafana/pkg/services/star" "github.com/grafana/grafana/pkg/services/supportbundles/supportbundlesimpl" "github.com/grafana/grafana/pkg/setting" @@ -36,7 +35,6 @@ type ServiceImpl struct { accesscontrolService ac.Service kvStore kvstore.KVStore apiKeyService apikey.Service - queryLibraryService querylibrary.HTTPService // Navigation navigationAppConfig map[string]NavigationAppConfig @@ -50,7 +48,7 @@ type NavigationAppConfig struct { Icon string } -func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore plugins.Store, pluginSettings pluginsettings.Service, starService star.Service, features *featuremgmt.FeatureManager, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service, queryLibraryService querylibrary.HTTPService) navtree.Service { +func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore plugins.Store, pluginSettings pluginsettings.Service, starService star.Service, features *featuremgmt.FeatureManager, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service) navtree.Service { service := &ServiceImpl{ cfg: cfg, log: log.New("navtree service"), @@ -63,7 +61,6 @@ func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStor accesscontrolService: accesscontrolService, kvStore: kvStore, apiKeyService: apiKeyService, - queryLibraryService: queryLibraryService, } service.readNavigationSettings() @@ -129,18 +126,6 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, hasEditPerm bool, p }) } - if !s.queryLibraryService.IsDisabled() { - treeRoot.AddSection(&navtree.NavLink{ - Text: "Query Library", - Id: "query", - SubTitle: "Store, import, export and manage your team queries in an easy way.", - Icon: "file-search-alt", - SortWeight: navtree.WeightQueryLibrary, - Section: navtree.NavSectionCore, - Url: s.cfg.AppSubURL + "/query-library", - }) - } - if setting.ProfileEnabled && c.IsSignedIn { treeRoot.AddSection(s.getProfileNode(c)) } diff --git a/pkg/services/querylibrary/querylibraryimpl/http.go b/pkg/services/querylibrary/querylibraryimpl/http.go deleted file mode 100644 index 10ee1d1fc91..00000000000 --- a/pkg/services/querylibrary/querylibraryimpl/http.go +++ /dev/null @@ -1,87 +0,0 @@ -package querylibraryimpl - -import ( - "encoding/json" - "fmt" - "io" - "strings" - - "github.com/grafana/grafana/pkg/api/response" - "github.com/grafana/grafana/pkg/api/routing" - "github.com/grafana/grafana/pkg/middleware" - contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" - "github.com/grafana/grafana/pkg/services/querylibrary" -) - -type queriesServiceHTTPHandler struct { - service querylibrary.Service -} - -func (s *queriesServiceHTTPHandler) IsDisabled() bool { - return s.service.IsDisabled() -} - -func (s *queriesServiceHTTPHandler) delete(c *contextmodel.ReqContext) response.Response { - uid := c.Query("uid") - err := s.service.Delete(c.Req.Context(), c.SignedInUser, uid) - if err != nil { - return response.Error(500, fmt.Sprintf("error deleting query with id %s", uid), err) - } - - return response.JSON(200, map[string]interface{}{ - "success": true, - }) -} - -func (s *queriesServiceHTTPHandler) RegisterHTTPRoutes(routes routing.RouteRegister) { - reqSignedIn := middleware.ReqSignedIn - routes.Get("/", reqSignedIn, routing.Wrap(s.getBatch)) - routes.Post("/", reqSignedIn, routing.Wrap(s.update)) - routes.Delete("/", reqSignedIn, routing.Wrap(s.delete)) -} - -func (s *queriesServiceHTTPHandler) getBatch(c *contextmodel.ReqContext) response.Response { - uids := c.QueryStrings("uid") - - queries, err := s.service.GetBatch(c.Req.Context(), c.SignedInUser, uids) - if err != nil { - return response.Error(500, fmt.Sprintf("error retrieving queries: [%s]", strings.Join(uids, ",")), err) - } - - return response.JSON(200, queries) -} - -func (s *queriesServiceHTTPHandler) update(c *contextmodel.ReqContext) response.Response { - body, err := io.ReadAll(c.Req.Body) - if err != nil { - return response.Error(500, "error reading bytes", err) - } - - query := &querylibrary.Query{} - err = json.Unmarshal(body, query) - if err != nil { - return response.Error(400, "error parsing body", err) - } - - if err := s.service.Update(c.Req.Context(), c.SignedInUser, query); err != nil { - var msg string - if len(query.UID) > 0 { - msg = fmt.Sprintf("error updating query with UID %s: %s", query.UID, err.Error()) - } else { - msg = fmt.Sprintf("error updating query with: %s", err.Error()) - } - return response.Error(500, msg, err) - } - - return response.JSON(200, map[string]interface{}{ - "success": true, - }) -} - -func ProvideHTTPService( - queriesService querylibrary.Service, -) querylibrary.HTTPService { - return &queriesServiceHTTPHandler{ - service: queriesService, - } -} diff --git a/pkg/services/querylibrary/querylibraryimpl/service.go b/pkg/services/querylibrary/querylibraryimpl/service.go deleted file mode 100644 index 15b39bea04f..00000000000 --- a/pkg/services/querylibrary/querylibraryimpl/service.go +++ /dev/null @@ -1,298 +0,0 @@ -package querylibraryimpl - -import ( - "context" - "fmt" - "strings" - - "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/infra/x/persistentcollection" - "github.com/grafana/grafana/pkg/services/dashboards" - "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/grafana/grafana/pkg/services/querylibrary" - "github.com/grafana/grafana/pkg/services/store/kind/dashboard" - "github.com/grafana/grafana/pkg/services/user" - "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/util" -) - -func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles) querylibrary.Service { - return &service{ - cfg: cfg, - log: log.New("queryLibraryService"), - features: features, - collection: persistentcollection.NewLocalFSPersistentCollection[*querylibrary.Query]("query-library", cfg.DataPath, 1), - } -} - -type service struct { - cfg *setting.Cfg - features featuremgmt.FeatureToggles - log log.Logger - collection persistentcollection.PersistentCollection[*querylibrary.Query] -} - -type perRequestQueryLoader struct { - service querylibrary.Service - queries map[string]*querylibrary.Query - ctx context.Context - user *user.SignedInUser -} - -func (q *perRequestQueryLoader) byUID(uid string) (*querylibrary.Query, error) { - if q, ok := q.queries[uid]; ok { - return q, nil - } - - queries, err := q.service.GetBatch(q.ctx, q.user, []string{uid}) - if err != nil { - return nil, err - } - - if len(queries) != 1 { - return nil, err - } - - q.queries[uid] = queries[0] - return queries[0], nil -} - -func newPerRequestQueryLoader(ctx context.Context, user *user.SignedInUser, service querylibrary.Service) queryLoader { - return &perRequestQueryLoader{queries: make(map[string]*querylibrary.Query), ctx: ctx, user: user, service: service} -} - -type queryLoader interface { - byUID(uid string) (*querylibrary.Query, error) -} - -func (s *service) UpdateDashboardQueries(ctx context.Context, user *user.SignedInUser, dash *dashboards.Dashboard) error { - queryLoader := newPerRequestQueryLoader(ctx, user, s) - return s.updateQueriesRecursively(queryLoader, dash.Data) -} - -func (s *service) updateQueriesRecursively(loader queryLoader, parent *simplejson.Json) error { - panels := parent.Get("panels").MustArray() - for i := range panels { - panelAsJSON := simplejson.NewFromAny(panels[i]) - panelType := panelAsJSON.Get("type").MustString() - - if panelType == "row" { - err := s.updateQueriesRecursively(loader, panelAsJSON) - if err != nil { - return err - } - continue - } - - queryUID := panelAsJSON.GetPath("savedQueryLink", "ref", "uid").MustString() - if queryUID == "" { - continue - } - - query, err := loader.byUID(queryUID) - if err != nil { - return err - } - - if query == nil { - // query deleted - unlink - panelAsJSON.Set("savedQueryLink", nil) - continue - } - - queriesAsMap := make([]interface{}, 0) - for idx := range query.Queries { - queriesAsMap = append(queriesAsMap, query.Queries[idx].MustMap()) - } - panelAsJSON.Set("targets", queriesAsMap) - - isMixed, firstDsRef := isQueryWithMixedDataSource(query) - if isMixed { - panelAsJSON.Set("datasource", map[string]interface{}{ - "uid": "-- Mixed --", - "type": "datasource", - }) - } else { - panelAsJSON.Set("datasource", map[string]interface{}{ - "uid": firstDsRef.UID, - "type": firstDsRef.Type, - }) - } - } - - return nil -} - -func (s *service) IsDisabled() bool { - return !s.features.IsEnabled(featuremgmt.FlagQueryLibrary) || !s.features.IsEnabled(featuremgmt.FlagPanelTitleSearch) -} - -func namespaceFromUser(user *user.SignedInUser) string { - return fmt.Sprintf("orgId-%d", user.OrgID) -} - -func (s *service) Search(ctx context.Context, user *user.SignedInUser, options querylibrary.QuerySearchOptions) ([]querylibrary.QueryInfo, error) { - queries, err := s.collection.Find(ctx, namespaceFromUser(user), func(_ *querylibrary.Query) (bool, error) { return true, nil }) - if err != nil { - return nil, err - } - - queryInfo := asQueryInfo(queries) - filteredQueryInfo := make([]querylibrary.QueryInfo, 0) - for _, q := range queryInfo { - if len(options.Query) > 0 { - lowerTitle := strings.ReplaceAll(strings.ToLower(q.Title), " ", "") - lowerQuery := strings.ReplaceAll(strings.ToLower(options.Query), " ", "") - - if !strings.Contains(lowerTitle, lowerQuery) { - continue - } - } - - if len(options.DatasourceUID) > 0 || len(options.DatasourceType) > 0 { - dsUids := make(map[string]bool) - dsTypes := make(map[string]bool) - for _, ds := range q.Datasource { - dsUids[ds.UID] = true - dsTypes[ds.Type] = true - } - - if len(options.DatasourceType) > 0 && !dsTypes[options.DatasourceType] { - continue - } - - if len(options.DatasourceUID) > 0 && !dsUids[options.DatasourceUID] { - continue - } - } - - filteredQueryInfo = append(filteredQueryInfo, q) - } - - return filteredQueryInfo, nil -} - -func asQueryInfo(queries []*querylibrary.Query) []querylibrary.QueryInfo { - res := make([]querylibrary.QueryInfo, 0) - for _, query := range queries { - res = append(res, querylibrary.QueryInfo{ - UID: query.UID, - Title: query.Title, - Description: query.Description, - Tags: query.Tags, - TimeFrom: query.Time.From, - TimeTo: query.Time.To, - SchemaVersion: query.SchemaVersion, - Datasource: extractDataSources(query), - }) - } - return res -} - -func getDatasourceUID(q *simplejson.Json) string { - uid := q.Get("datasource").Get("uid").MustString() - - if uid == "" { - uid = q.Get("datasource").MustString() - } - - if expr.IsDataSource(uid) { - return expr.DatasourceUID - } - - return uid -} - -func isQueryWithMixedDataSource(q *querylibrary.Query) (isMixed bool, firstDsRef dashboard.DataSourceRef) { - dsRefs := extractDataSources(q) - - for _, dsRef := range dsRefs { - if dsRef.Type == expr.DatasourceType { - continue - } - - if firstDsRef.UID == "" { - firstDsRef = dsRef - continue - } - - if firstDsRef.UID != dsRef.UID || firstDsRef.Type != dsRef.Type { - return true, firstDsRef - } - } - - return false, firstDsRef -} - -func extractDataSources(query *querylibrary.Query) []dashboard.DataSourceRef { - ds := make([]dashboard.DataSourceRef, 0) - - for _, q := range query.Queries { - dsUid := getDatasourceUID(q) - dsType := q.Get("datasource").Get("type").MustString() - if expr.IsDataSource(dsUid) { - dsType = expr.DatasourceType - } - - ds = append(ds, dashboard.DataSourceRef{ - UID: dsUid, - Type: dsType, - }) - } - - return ds -} - -func (s *service) GetBatch(ctx context.Context, user *user.SignedInUser, uids []string) ([]*querylibrary.Query, error) { - uidMap := make(map[string]bool) - for _, uid := range uids { - uidMap[uid] = true - } - - return s.collection.Find(ctx, namespaceFromUser(user), func(q *querylibrary.Query) (bool, error) { - if _, ok := uidMap[q.UID]; ok { - return true, nil - } - - return false, nil - }) -} - -func (s *service) Update(ctx context.Context, user *user.SignedInUser, query *querylibrary.Query) error { - if query.UID == "" { - queriesWithTheSameTitle, err := s.Search(ctx, user, querylibrary.QuerySearchOptions{Query: query.Title}) - if err != nil { - return err - } - - if len(queriesWithTheSameTitle) != 0 { - return fmt.Errorf("can't create query with title '%s'. existing query with similar name: '%s'", query.Title, queriesWithTheSameTitle[0].Title) - } - - query.UID = util.GenerateShortUID() - return s.collection.Insert(ctx, namespaceFromUser(user), query) - } - - _, err := s.collection.Update(ctx, namespaceFromUser(user), func(q *querylibrary.Query) (updated bool, updatedItem *querylibrary.Query, err error) { - if q.UID == query.UID { - return true, query, nil - } - - return false, nil, nil - }) - return err -} - -func (s *service) Delete(ctx context.Context, user *user.SignedInUser, uid string) error { - _, err := s.collection.Delete(ctx, namespaceFromUser(user), func(q *querylibrary.Query) (bool, error) { - if q.UID == uid { - return true, nil - } - - return false, nil - }) - - return err -} diff --git a/pkg/services/querylibrary/tests/api_client.go b/pkg/services/querylibrary/tests/api_client.go deleted file mode 100644 index ec7a769ec33..00000000000 --- a/pkg/services/querylibrary/tests/api_client.go +++ /dev/null @@ -1,286 +0,0 @@ -package querylibrary_tests - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - - "github.com/grafana/grafana/pkg/api/dtos" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/infra/db" - "github.com/grafana/grafana/pkg/services/querylibrary" - "github.com/grafana/grafana/pkg/services/user" -) - -type queryLibraryAPIClient struct { - token string - url string - user *user.SignedInUser - sqlStore db.DB -} - -func newQueryLibraryAPIClient(token string, baseUrl string, user *user.SignedInUser, sqlStore db.DB) *queryLibraryAPIClient { - return &queryLibraryAPIClient{ - token: token, - url: baseUrl, - user: user, - sqlStore: sqlStore, - } -} - -func (q *queryLibraryAPIClient) update(ctx context.Context, query *querylibrary.Query) error { - buf := bytes.Buffer{} - enc := json.NewEncoder(&buf) - err := enc.Encode(query) - if err != nil { - return err - } - - url := fmt.Sprintf("%s/query-library", q.url) - - req, err := http.NewRequestWithContext(ctx, "POST", url, &buf) - if err != nil { - return err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", q.token)) - req.Header.Set("Content-Type", "application/json") - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return err - } - - _ = resp.Body.Close() - return nil -} - -func (q *queryLibraryAPIClient) delete(ctx context.Context, uid string) error { - url := fmt.Sprintf("%s/query-library?uid=%s", q.url, uid) - - req, err := http.NewRequestWithContext(ctx, "DELETE", url, bytes.NewBuffer([]byte(""))) - if err != nil { - return err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", q.token)) - req.Header.Set("Content-Type", "application/json") - client := &http.Client{} - resp, err := client.Do(req) - defer func() { - _ = resp.Body.Close() - }() - - return err -} - -func (q *queryLibraryAPIClient) get(ctx context.Context, uid string) (*querylibrary.Query, error) { - url := fmt.Sprintf("%s/query-library?uid=%s", q.url, uid) - - req, err := http.NewRequestWithContext(ctx, "GET", url, bytes.NewBuffer([]byte(""))) - if err != nil { - return nil, err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", q.token)) - req.Header.Set("Content-Type", "application/json") - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer func() { - _ = resp.Body.Close() - }() - - b, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - query := make([]*querylibrary.Query, 0) - err = json.Unmarshal(b, &query) - if len(query) > 0 { - return query[0], err - } - - return nil, err -} - -type querySearchInfo struct { - kind string - uid string - name string - dsUIDs []string - location string -} - -func (q *queryLibraryAPIClient) search(ctx context.Context, options querylibrary.QuerySearchOptions) ([]*querySearchInfo, error) { - return q.searchRetry(ctx, options, 1) -} - -func (q *queryLibraryAPIClient) searchRetry(ctx context.Context, options querylibrary.QuerySearchOptions, attempt int) ([]*querySearchInfo, error) { - if attempt >= 3 { - return nil, errors.New("max attempts") - } - - url := fmt.Sprintf("%s/search-v2", q.url) - - text := "*" - if options.Query != "" { - text = options.Query - } - - searchReq := map[string]interface{}{ - "query": text, - "sort": "name_sort", - "kind": []string{"query"}, - "limit": 50, - } - - searchReqJson, err := simplejson.NewFromAny(searchReq).MarshalJSON() - if err != nil { - return nil, err - } - - req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(searchReqJson)) - - if err != nil { - return nil, err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", q.token)) - req.Header.Set("Content-Type", "application/json") - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer func() { - _ = resp.Body.Close() - }() - - b, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - r := &backend.DataResponse{} - err = json.Unmarshal(b, r) - - if len(r.Frames) != 1 { - return nil, fmt.Errorf("expected a single frame, received %s", string(b)) - } - - frame := r.Frames[0] - if frame.Name == "Loading" { - time.Sleep(100 * time.Millisecond) - return q.searchRetry(ctx, options, attempt+1) - } - - res := make([]*querySearchInfo, 0) - - frameLen, _ := frame.RowLen() - for i := 0; i < frameLen; i++ { - fKind, _ := frame.FieldByName("kind") - fUid, _ := frame.FieldByName("uid") - fName, _ := frame.FieldByName("name") - dsUID, _ := frame.FieldByName("ds_uid") - fLocation, _ := frame.FieldByName("location") - - rawValue, ok := dsUID.At(i).(json.RawMessage) - if !ok || rawValue == nil { - return nil, errors.New("invalid ds_uid field") - } - - jsonValue, err := rawValue.MarshalJSON() - if err != nil { - return nil, err - } - - var uids []string - err = json.Unmarshal(jsonValue, &uids) - if err != nil { - return nil, err - } - - res = append(res, &querySearchInfo{ - kind: fKind.At(i).(string), - uid: fUid.At(i).(string), - name: fName.At(i).(string), - dsUIDs: uids, - location: fLocation.At(i).(string), - }) - } - return res, err -} - -func (q *queryLibraryAPIClient) getDashboard(ctx context.Context, uid string) (*dtos.DashboardFullWithMeta, error) { - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/dashboards/uid/%s", q.url, uid), bytes.NewBuffer([]byte(""))) - if err != nil { - return nil, err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", q.token)) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer func() { - _ = resp.Body.Close() - }() - - b, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - res := &dtos.DashboardFullWithMeta{} - err = json.Unmarshal(b, res) - if err != nil { - return nil, err - } - - return res, nil -} - -func (q *queryLibraryAPIClient) createDashboard(ctx context.Context, dash *simplejson.Json) (string, error) { - buf := bytes.Buffer{} - enc := json.NewEncoder(&buf) - dashMap, err := dash.Map() - if err != nil { - return "", err - } - err = enc.Encode(dashMap) - if err != nil { - return "", err - } - - url := fmt.Sprintf("%s/dashboards/db", q.url) - - req, err := http.NewRequestWithContext(ctx, "POST", url, &buf) - if err != nil { - return "", err - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", q.token)) - req.Header.Set("Content-Type", "application/json") - client := &http.Client{} - resp, err := client.Do(req) - defer func() { - _ = resp.Body.Close() - }() - if err != nil { - return "", err - } - - jsonResp, err := simplejson.NewFromReader(resp.Body) - if err != nil { - return "", err - } - - return jsonResp.Get("uid").MustString(), nil -} diff --git a/pkg/services/querylibrary/tests/common.go b/pkg/services/querylibrary/tests/common.go deleted file mode 100644 index 77cb7ea47df..00000000000 --- a/pkg/services/querylibrary/tests/common.go +++ /dev/null @@ -1,74 +0,0 @@ -package querylibrary_tests - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - apikeygenprefix "github.com/grafana/grafana/pkg/components/apikeygenprefixed" - "github.com/grafana/grafana/pkg/server" - "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/grafana/grafana/pkg/services/org" - saAPI "github.com/grafana/grafana/pkg/services/serviceaccounts/api" - saTests "github.com/grafana/grafana/pkg/services/serviceaccounts/tests" - "github.com/grafana/grafana/pkg/services/user" - "github.com/grafana/grafana/pkg/tests/testinfra" -) - -func createServiceAccountAdminToken(t *testing.T, name string, env *server.TestEnv) (string, *user.SignedInUser) { - t.Helper() - - account := saTests.SetupUserServiceAccount(t, env.SQLStore, saTests.TestUser{ - Name: name, - Role: string(org.RoleAdmin), - Login: name, - IsServiceAccount: true, - }) - - keyGen, err := apikeygenprefix.New(saAPI.ServiceID) - require.NoError(t, err) - - _ = saTests.SetupApiKey(t, env.SQLStore, saTests.TestApiKey{ - Name: name, - Role: org.RoleAdmin, - OrgId: account.OrgID, - Key: keyGen.HashedKey, - ServiceAccountID: &account.ID, - }) - - return keyGen.ClientSecret, &user.SignedInUser{ - UserID: account.ID, - Email: account.Email, - Name: account.Name, - Login: account.Login, - OrgID: account.OrgID, - IsServiceAccount: true, - } -} - -type testContext struct { - authToken string - client *queryLibraryAPIClient - user *user.SignedInUser -} - -func createTestContext(t *testing.T) testContext { - t.Helper() - - dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ - EnableFeatureToggles: []string{featuremgmt.FlagPanelTitleSearch, featuremgmt.FlagQueryLibrary}, - QueryRetries: 3, - }) - grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path) - - authToken, serviceAccountUser := createServiceAccountAdminToken(t, "query-library", env) - - client := newQueryLibraryAPIClient(authToken, fmt.Sprintf("http://%s/api", grafanaListedAddr), serviceAccountUser, env.SQLStore) - - return testContext{ - authToken: authToken, - client: client, - user: serviceAccountUser, - } -} diff --git a/pkg/services/querylibrary/tests/querylibrary_integration_test.go b/pkg/services/querylibrary/tests/querylibrary_integration_test.go deleted file mode 100644 index b0112cd49db..00000000000 --- a/pkg/services/querylibrary/tests/querylibrary_integration_test.go +++ /dev/null @@ -1,299 +0,0 @@ -package querylibrary_tests - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/services/querylibrary" - "github.com/grafana/grafana/pkg/tsdb/grafanads" -) - -func TestIntegrationCreateAndDelete(t *testing.T) { - if true { - // TODO: re-enable after fixing its flakiness - t.Skip() - } - - if testing.Short() { - t.Skip("skipping integration test") - } - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - testCtx := createTestContext(t) - - err := testCtx.client.update(ctx, &querylibrary.Query{ - UID: "", - Title: "first query", - Tags: []string{}, - Description: "", - Time: querylibrary.Time{ - From: "now-15m", - To: "now-30m", - }, - Queries: []*simplejson.Json{ - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]string{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "randomWalk", - "refId": "A", - }), - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]string{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "list", - "path": "img", - "refId": "B", - }), - }, - Variables: []*simplejson.Json{}, - }) - require.NoError(t, err) - - search, err := testCtx.client.search(ctx, querylibrary.QuerySearchOptions{ - Query: "", - }) - require.NoError(t, err) - require.Len(t, search, 1) - - info := search[0] - require.Equal(t, "query", info.kind) - require.Equal(t, "first query", info.name) - require.Equal(t, "General", info.location) - require.Equal(t, []string{grafanads.DatasourceUID, grafanads.DatasourceUID}, info.dsUIDs) - - err = testCtx.client.delete(ctx, info.uid) - require.NoError(t, err) - - search, err = testCtx.client.search(ctx, querylibrary.QuerySearchOptions{ - Query: "", - }) - require.NoError(t, err) - require.Len(t, search, 0) - - query, err := testCtx.client.get(ctx, info.uid) - require.NoError(t, err) - require.Nil(t, query) -} - -func createQuery(t *testing.T, ctx context.Context, testCtx testContext) string { - t.Helper() - - err := testCtx.client.update(ctx, &querylibrary.Query{ - UID: "", - Title: "first query", - Tags: []string{}, - Description: "", - Time: querylibrary.Time{ - From: "now-15m", - To: "now-30m", - }, - Queries: []*simplejson.Json{ - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]string{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "randomWalk", - "refId": "A", - }), - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]string{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "list", - "path": "img", - "refId": "B", - }), - }, - Variables: []*simplejson.Json{}, - }) - require.NoError(t, err) - - search, err := testCtx.client.search(ctx, querylibrary.QuerySearchOptions{ - Query: "", - }) - require.NoError(t, err) - require.Len(t, search, 1) - return search[0].uid -} - -func TestIntegrationDashboardGetWithLatestSavedQueries(t *testing.T) { - if true { - // TODO: re-enable after fixing its flakiness - t.Skip() - } - - if testing.Short() { - t.Skip("skipping integration test") - } - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - testCtx := createTestContext(t) - - queryUID := createQuery(t, ctx, testCtx) - - dashUID, err := testCtx.client.createDashboard(ctx, simplejson.NewFromAny(map[string]interface{}{ - "dashboard": map[string]interface{}{ - "title": "my-new-dashboard", - "panels": []interface{}{ - map[string]interface{}{ - "id": int64(1), - "gridPos": map[string]interface{}{ - "h": 6, - "w": 6, - "x": 0, - "y": 0, - }, - }, - map[string]interface{}{ - "id": int64(2), - "gridPos": map[string]interface{}{ - "h": 6, - "w": 6, - "x": 6, - "y": 0, - }, - "savedQueryLink": map[string]interface{}{ - "ref": map[string]string{ - "uid": queryUID, - }, - }, - }, - }, - }, - "folderId": 0, - "message": "", - "overwrite": true, - })) - require.NoError(t, err) - - dashboard, err := testCtx.client.getDashboard(ctx, dashUID) - require.NoError(t, err) - - panelsAsArray, err := dashboard.Dashboard.Get("panels").Array() - require.NoError(t, err) - - require.Len(t, panelsAsArray, 2) - - secondPanel := simplejson.NewFromAny(panelsAsArray[1]) - require.Equal(t, []interface{}{ - map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "randomWalk", - "refId": "A", - }, - map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "list", - "path": "img", - "refId": "B", - }, - }, secondPanel.Get("targets").MustArray()) - require.Equal(t, map[string]interface{}{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, secondPanel.Get("datasource").MustMap()) - - // update, expect changes when getting dashboards - err = testCtx.client.update(ctx, &querylibrary.Query{ - UID: queryUID, - Title: "first query", - Tags: []string{}, - Description: "", - Time: querylibrary.Time{ - From: "now-15m", - To: "now-30m", - }, - Queries: []*simplejson.Json{ - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "randomWalk", - "refId": "A", - }), - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": "different-datasource-uid", - "type": "datasource", - }, - "queryType": "randomWalk", - "path": "img", - "refId": "B", - }), - simplejson.NewFromAny(map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": "different-datasource-uid-2", - "type": "datasource", - }, - "queryType": "randomWalk", - "path": "img", - "refId": "C", - }), - }, - Variables: []*simplejson.Json{}, - }) - require.NoError(t, err) - - dashboard, err = testCtx.client.getDashboard(ctx, dashUID) - require.NoError(t, err) - - panelsAsArray, err = dashboard.Dashboard.Get("panels").Array() - require.NoError(t, err) - - require.Len(t, panelsAsArray, 2) - - secondPanel = simplejson.NewFromAny(panelsAsArray[1]) - require.Equal(t, []interface{}{ - map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": grafanads.DatasourceUID, - "type": "datasource", - }, - "queryType": "randomWalk", - "refId": "A", - }, - map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": "different-datasource-uid", - "type": "datasource", - }, - "queryType": "randomWalk", - "path": "img", - "refId": "B", - }, - map[string]interface{}{ - "datasource": map[string]interface{}{ - "uid": "different-datasource-uid-2", - "type": "datasource", - }, - "queryType": "randomWalk", - "path": "img", - "refId": "C", - }, - }, secondPanel.Get("targets").MustArray()) - require.Equal(t, map[string]interface{}{ - "uid": "-- Mixed --", - "type": "datasource", - }, secondPanel.Get("datasource").MustMap()) -} diff --git a/pkg/services/querylibrary/types.go b/pkg/services/querylibrary/types.go deleted file mode 100644 index 4f2bd11cfc3..00000000000 --- a/pkg/services/querylibrary/types.go +++ /dev/null @@ -1,88 +0,0 @@ -package querylibrary - -import ( - "context" - - "github.com/grafana/grafana/pkg/api/routing" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/registry" - "github.com/grafana/grafana/pkg/services/dashboards" - "github.com/grafana/grafana/pkg/services/store/kind/dashboard" - "github.com/grafana/grafana/pkg/services/user" -) - -type Time struct { - // From Start time in epoch timestamps in milliseconds or relative using Grafana time units. - // required: true - // example: now-1h - From string `json:"from"` - - // To End time in epoch timestamps in milliseconds or relative using Grafana time units. - // required: true - // example: now - To string `json:"to"` -} - -type Query struct { - UID string `json:"uid"` - - Title string `json:"title"` - - Tags []string `json:"tags"` - - Description string `json:"description"` - - SchemaVersion int64 `json:"schemaVersion"` - - Time Time `json:"time"` - - // queries.refId – Specifies an identifier of the query. Is optional and default to “A”. - // queries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId. - // queries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100. - // queries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000. - // required: true - // example: [ { "refId": "A", "intervalMs": 86400000, "maxDataPoints": 1092, "datasource":{ "uid":"PD8C576611E62080A" }, "rawSql": "SELECT 1 as valueOne, 2 as valueTwo", "format": "table" } ] - Queries []*simplejson.Json `json:"queries"` - - Variables []*simplejson.Json `json:"variables"` -} - -type SavedQueryRef struct { - UID string `json:"uid"` -} - -type SavedQueryLink struct { - Ref SavedQueryRef `json:"ref"` -} - -type QueryInfo struct { - UID string `json:"uid"` - Title string `json:"title"` - Description string `json:"description"` - Tags []string `json:"tags"` - TimeFrom string `json:"timeFrom"` - TimeTo string `json:"timeTo"` - SchemaVersion int64 `json:"schemaVersion"` - - Datasource []dashboard.DataSourceRef `json:"datasource,omitempty"` // UIDs -} - -type QuerySearchOptions struct { - DatasourceUID string - Query string - DatasourceType string -} - -type Service interface { - Search(ctx context.Context, user *user.SignedInUser, options QuerySearchOptions) ([]QueryInfo, error) - GetBatch(ctx context.Context, user *user.SignedInUser, uids []string) ([]*Query, error) - Update(ctx context.Context, user *user.SignedInUser, query *Query) error - Delete(ctx context.Context, user *user.SignedInUser, uid string) error - UpdateDashboardQueries(ctx context.Context, user *user.SignedInUser, dash *dashboards.Dashboard) error - registry.CanBeDisabled -} - -type HTTPService interface { - registry.CanBeDisabled - RegisterHTTPRoutes(routes routing.RouteRegister) -} diff --git a/pkg/services/searchV2/allowed_actions_test.go b/pkg/services/searchV2/allowed_actions_test.go index b73adc5c23a..933fd6fcefe 100644 --- a/pkg/services/searchV2/allowed_actions_test.go +++ b/pkg/services/searchV2/allowed_actions_test.go @@ -86,7 +86,7 @@ var ( func service(t *testing.T) *StandardSearchService { service, ok := ProvideService(&setting.Cfg{Search: setting.SearchSettings{}}, nil, nil, accesscontrolmock.New(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(), - nil, nil, nil, nil).(*StandardSearchService) + nil, nil, nil).(*StandardSearchService) require.True(t, ok) return service } diff --git a/pkg/services/searchV2/queries.go b/pkg/services/searchV2/queries.go deleted file mode 100644 index e7104e26640..00000000000 --- a/pkg/services/searchV2/queries.go +++ /dev/null @@ -1,113 +0,0 @@ -package searchV2 - -import ( - "context" - "encoding/json" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/data" - - "github.com/grafana/grafana/pkg/services/querylibrary" - "github.com/grafana/grafana/pkg/services/user" -) - -// TEMPORARY FILE - -func (s *StandardSearchService) searchQueries(ctx context.Context, user *user.SignedInUser, q DashboardQuery) *backend.DataResponse { - queryText := q.Query - if queryText == "*" { - queryText = "" - } - queryInfo, err := s.queries.Search(ctx, user, querylibrary.QuerySearchOptions{ - Query: queryText, - DatasourceUID: q.Datasource, - DatasourceType: q.DatasourceType, - }) - if err != nil { - return &backend.DataResponse{Error: err} - } - - header := &customMeta{ - SortBy: q.Sort, - Count: uint64(len(queryInfo)), - } - - fScore := data.NewFieldFromFieldType(data.FieldTypeFloat64, 0) - fUID := data.NewFieldFromFieldType(data.FieldTypeString, 0) - fKind := data.NewFieldFromFieldType(data.FieldTypeString, 0) - fPType := data.NewFieldFromFieldType(data.FieldTypeString, 0) - fName := data.NewFieldFromFieldType(data.FieldTypeString, 0) - fURL := data.NewFieldFromFieldType(data.FieldTypeString, 0) - fLocation := data.NewFieldFromFieldType(data.FieldTypeString, 0) - fTags := data.NewFieldFromFieldType(data.FieldTypeNullableJSON, 0) - fDSUIDs := data.NewFieldFromFieldType(data.FieldTypeJSON, 0) - fExplain := data.NewFieldFromFieldType(data.FieldTypeNullableJSON, 0) - - fScore.Name = "score" - fUID.Name = "uid" - fKind.Name = "kind" - fName.Name = "name" - fLocation.Name = "location" - fURL.Name = "url" - fURL.Config = &data.FieldConfig{ - Links: []data.DataLink{ - {Title: "link", URL: "${__value.text}"}, - }, - } - fPType.Name = "panel_type" - fDSUIDs.Name = "ds_uid" - fTags.Name = "tags" - fExplain.Name = "explain" - - frame := data.NewFrame("Query results", fKind, fUID, fName, fPType, fURL, fTags, fDSUIDs, fLocation) - if q.Explain { - frame.Fields = append(frame.Fields, fScore, fExplain) - } - frame.SetMeta(&data.FrameMeta{ - Type: "search-results", - Custom: header, - }) - - fieldLen := 0 - - for _, q := range queryInfo { - fKind.Append(string(entityKindQuery)) - fUID.Append(q.UID) - fPType.Append("") - fName.Append(q.Title) - fURL.Append("") - fLocation.Append("General") - - tags := q.Tags - if tags == nil { - tags = make([]string, 0) - } - - tagsJson := mustJsonRawMessage(tags) - fTags.Append(&tagsJson) - - dsUids := make([]string, 0) - for _, dsRef := range q.Datasource { - dsUids = append(dsUids, dsRef.UID) - } - - fDSUIDs.Append(mustJsonRawMessage(dsUids)) - - // extend fields to match the longest field - fieldLen++ - for _, f := range frame.Fields { - if fieldLen > f.Len() { - f.Extend(fieldLen - f.Len()) - } - } - } - - return &backend.DataResponse{ - Frames: data.Frames{frame}, - } -} - -func mustJsonRawMessage(arr []string) json.RawMessage { - js, _ := json.Marshal(arr) - return js -} diff --git a/pkg/services/searchV2/service.go b/pkg/services/searchV2/service.go index 4c8ad4d248a..674cfb924aa 100644 --- a/pkg/services/searchV2/service.go +++ b/pkg/services/searchV2/service.go @@ -18,7 +18,6 @@ import ( "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/org" - "github.com/grafana/grafana/pkg/services/querylibrary" "github.com/grafana/grafana/pkg/services/store" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" @@ -75,7 +74,6 @@ type StandardSearchService struct { dashboardIndex *searchIndex extender DashboardIndexExtender reIndexCh chan struct{} - queries querylibrary.Service features featuremgmt.FeatureToggles } @@ -85,7 +83,7 @@ func (s *StandardSearchService) IsReady(ctx context.Context, orgId int64) IsSear func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEventsService, ac accesscontrol.Service, tracer tracing.Tracer, features featuremgmt.FeatureToggles, orgService org.Service, - userService user.Service, queries querylibrary.Service, folderService folder.Service) SearchService { + userService user.Service, folderService folder.Service) SearchService { extender := &NoopExtender{} logger := log.New("searchV2") s := &StandardSearchService{ @@ -112,7 +110,6 @@ func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEv reIndexCh: make(chan struct{}, 1), orgService: orgService, userService: userService, - queries: queries, features: features, } return s @@ -242,10 +239,6 @@ func (s *StandardSearchService) DoDashboardQuery(ctx context.Context, user *back } func (s *StandardSearchService) doDashboardQuery(ctx context.Context, signedInUser *user.SignedInUser, orgID int64, q DashboardQuery) *backend.DataResponse { - if !s.queries.IsDisabled() && len(q.Kind) == 1 && q.Kind[0] == string(entityKindQuery) { - return s.searchQueries(ctx, signedInUser, q) - } - rsp := &backend.DataResponse{} filter, err := s.auth.GetDashboardReadFilter(ctx, orgID, signedInUser) diff --git a/pkg/services/searchV2/service_bench_test.go b/pkg/services/searchV2/service_bench_test.go index fee2167c271..01e4ca4be4d 100644 --- a/pkg/services/searchV2/service_bench_test.go +++ b/pkg/services/searchV2/service_bench_test.go @@ -15,7 +15,6 @@ import ( "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org/orgtest" - "github.com/grafana/grafana/pkg/services/querylibrary/querylibraryimpl" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/store" "github.com/grafana/grafana/pkg/services/user" @@ -37,9 +36,8 @@ func setupBenchEnv(b *testing.B, folderCount, dashboardsPerFolder int) (*Standar orgSvc := &orgtest.FakeOrgService{ ExpectedOrgs: []*org.OrgDTO{{ID: 1}}, } - querySvc := querylibraryimpl.ProvideService(cfg, features) searchService, ok := ProvideService(cfg, sqlStore, store.NewDummyEntityEventsService(), actest.FakeService{}, - tracing.InitializeTracerForTest(), features, orgSvc, nil, querySvc, nil).(*StandardSearchService) + tracing.InitializeTracerForTest(), features, orgSvc, nil, nil).(*StandardSearchService) require.True(b, ok) err = runSearchService(searchService) diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorQueries.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorQueries.tsx index 28bf9147077..7788b448276 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorQueries.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorQueries.tsx @@ -37,7 +37,6 @@ export class PanelEditorQueries extends PureComponent { queries: panel.targets, maxDataPoints: panel.maxDataPoints, minInterval: panel.interval, - savedQueryUid: panel.savedQueryLink?.ref.uid ?? null, // Used by experimental feature queryLibrary timeRange: { from: panel.timeFrom, shift: panel.timeShift, diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index 44ba8af7210..b01795f5126 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -27,7 +27,6 @@ import { LibraryPanel, LibraryPanelRef } from '@grafana/schema'; import config from 'app/core/config'; import { safeStringifyValue } from 'app/core/utils/explore'; import { getNextRefIdChar } from 'app/core/utils/query'; -import { SavedQueryLink } from 'app/features/query-library/types'; import { QueryGroupOptions } from 'app/types'; import { PanelOptionsChangedEvent, @@ -131,7 +130,6 @@ const defaults: any = { overrides: [], }, title: '', - savedQueryLink: null, }; export class PanelModel implements DataConfigSource, IPanelModel { @@ -156,8 +154,6 @@ export class PanelModel implements DataConfigSource, IPanelModel { datasource: DataSourceRef | null = null; thresholds?: any; pluginVersion?: string; - savedQueryLink: SavedQueryLink | null = null; // Used by the experimental feature queryLibrary - snapshotData?: DataFrameDTO[]; timeFrom?: any; timeShift?: any; @@ -524,17 +520,6 @@ export class PanelModel implements DataConfigSource, IPanelModel { type: dataSource.type, }; - if (options.savedQueryUid) { - this.savedQueryLink = { - ref: { - uid: options.savedQueryUid, - }, - variables: [], - }; - } else { - this.savedQueryLink = null; - } - this.cacheTimeout = options.cacheTimeout; this.queryCachingTTL = options.queryCachingTTL; this.timeFrom = options.timeRange?.from; diff --git a/public/app/features/query-library/api/SavedQueriesApi.ts b/public/app/features/query-library/api/SavedQueriesApi.ts deleted file mode 100644 index 54544a65f9e..00000000000 --- a/public/app/features/query-library/api/SavedQueriesApi.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; - -import { DataQuery } from '@grafana/data/src'; - -import { SavedQueryUpdateOpts } from '../components/QueryEditorDrawer'; - -import { getSavedQuerySrv } from './SavedQueriesSrv'; - -export type SavedQueryRef = { - uid?: string; -}; - -export type Variable = { - name: string; - type?: string; - current: { - value: string | number; - }; -}; - -type SavedQueryMeta = { - title: string; - description?: string; - tags?: string[]; - schemaVersion?: number; - variables: Variable[]; -}; - -type SavedQueryData = { - queries: TQuery[]; -}; - -export type SavedQuery = SavedQueryMeta & SavedQueryData & SavedQueryRef; - -export const isQueryWithMixedDatasource = (savedQuery: SavedQuery): boolean => { - if (!savedQuery?.queries?.length) { - return false; - } - - const firstDs = savedQuery.queries[0].datasource; - return savedQuery.queries.some((q) => q.datasource?.uid !== firstDs?.uid || q.datasource?.type !== firstDs?.type); -}; - -const api = createApi({ - reducerPath: 'savedQueries', - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - endpoints: (build) => ({ - getSavedQueryByUids: build.query({ - async queryFn(arg, queryApi, extraOptions, baseQuery) { - return { data: await getSavedQuerySrv().getSavedQueries(arg) }; - }, - }), - deleteSavedQuery: build.mutation({ - async queryFn(arg) { - await getSavedQuerySrv().deleteSavedQuery(arg); - return { - data: null, - }; - }, - }), - updateSavedQuery: build.mutation({ - async queryFn(arg) { - await getSavedQuerySrv().updateSavedQuery(arg.query, arg.opts); - return { - data: null, - }; - }, - }), - }), -}); - -export const { useUpdateSavedQueryMutation } = api; diff --git a/public/app/features/query-library/api/SavedQueriesSrv.ts b/public/app/features/query-library/api/SavedQueriesSrv.ts deleted file mode 100644 index 122077626ee..00000000000 --- a/public/app/features/query-library/api/SavedQueriesSrv.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { getBackendSrv } from 'app/core/services/backend_srv'; -import { SavedQueryUpdateOpts } from 'app/features/query-library/components/QueryEditorDrawer'; - -import { SavedQuery, SavedQueryRef } from './SavedQueriesApi'; - -export class SavedQuerySrv { - getSavedQueries = async (refs: SavedQueryRef[]): Promise => { - if (!refs.length) { - return []; - } - const uidParams = refs.map((r) => `uid=${r.uid}`).join('&'); - return getBackendSrv().get(`/api/query-library?${uidParams}`); - }; - - deleteSavedQuery = async (ref: SavedQueryRef): Promise => { - return getBackendSrv().delete(`/api/query-library?uid=${ref.uid}`); - }; - - updateSavedQuery = async (query: SavedQuery, options: SavedQueryUpdateOpts): Promise => { - return getBackendSrv().post(`/api/query-library`, query); - }; -} - -const savedQuerySrv = new SavedQuerySrv(); - -export const getSavedQuerySrv = () => savedQuerySrv; diff --git a/public/app/features/query-library/components/CreateNewQuery.tsx b/public/app/features/query-library/components/CreateNewQuery.tsx deleted file mode 100644 index 8f05e018117..00000000000 --- a/public/app/features/query-library/components/CreateNewQuery.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { css } from '@emotion/css'; -import React, { useState } from 'react'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Button, CodeEditor, useStyles2 } from '@grafana/ui'; - -import { SavedQuery, useUpdateSavedQueryMutation } from '../api/SavedQueriesApi'; - -import { SavedQueryUpdateOpts } from './QueryEditorDrawer'; - -type Props = { - options: SavedQueryUpdateOpts; - onDismiss: () => void; - updateComponent?: () => void; -}; - -interface QueryForm { - val: SavedQuery; -} - -const initialForm: QueryForm = { - val: { - title: 'ds-variables', - tags: [], - description: 'example description', - schemaVersion: 1, - time: { - from: 'now-6h', - to: 'now', - }, - variables: [ - { - name: 'var1', - type: 'text', - current: { - value: 'hello world', - }, - }, - ], - queries: [ - { - // @ts-ignore - channel: 'plugin/testdata/random-flakey-stream', - datasource: { - type: 'datasource', - uid: 'grafana', - }, - filter: { - fields: ['Time', 'Value'], - }, - queryType: 'measurements', - refId: 'A', - search: { - query: '', - }, - }, - { - // @ts-ignore - alias: 'my-alias', - datasource: { - type: 'testdata', - uid: 'PD8C576611E62080A', - }, - drop: 11, - hide: false, - max: 1000, - min: 10, - noise: 5, - refId: 'B', - scenarioId: 'random_walk', - startValue: 10, - }, - ], - }, -}; - -export const CreateNewQuery = ({ onDismiss, updateComponent, options }: Props) => { - const styles = useStyles2(getStyles); - - const [updateSavedQuery] = useUpdateSavedQueryMutation(); - - const [query, setQuery] = useState(initialForm); - - return ( - <> - setQuery(() => ({ val: JSON.parse(val) }))} - onSave={(val) => setQuery(() => ({ val: JSON.parse(val) }))} - readOnly={false} - /> - - - - ); -}; - -export const getStyles = (theme: GrafanaTheme2) => { - return { - editor: css``, - submitButton: css` - align-self: flex-end; - margin-bottom: 25px; - margin-top: 25px; - `, - }; -}; diff --git a/public/app/features/query-library/components/DatasourceTypePicker.tsx b/public/app/features/query-library/components/DatasourceTypePicker.tsx deleted file mode 100644 index e5c34bd52f4..00000000000 --- a/public/app/features/query-library/components/DatasourceTypePicker.tsx +++ /dev/null @@ -1,113 +0,0 @@ -// Libraries -import { uniqBy } from 'lodash'; -import React from 'react'; - -// Components -import { DataSourceInstanceSettings, isUnsignedPluginSignature } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; -import { getDataSourceSrv } from '@grafana/runtime/src'; -import { HorizontalGroup, PluginSignatureBadge, Select } from '@grafana/ui'; - -export type DatasourceTypePickerProps = { - onChange: (ds: string | null) => void; - current: string | null; // type - hideTextValue?: boolean; - onBlur?: () => void; - autoFocus?: boolean; - openMenuOnFocus?: boolean; - placeholder?: string; - tracing?: boolean; - mixed?: boolean; - dashboard?: boolean; - metrics?: boolean; - type?: string | string[]; - annotations?: boolean; - variables?: boolean; - alerting?: boolean; - pluginId?: string; - /** If true,we show only DSs with logs; and if true, pluginId shouldnt be passed in */ - logs?: boolean; - width?: number; - inputId?: string; - filter?: (dataSource: DataSourceInstanceSettings) => boolean; - onClear?: () => void; -}; - -const getDataSourceTypeOptions = (props: DatasourceTypePickerProps) => { - const { alerting, tracing, metrics, mixed, dashboard, variables, annotations, pluginId, type, filter, logs } = props; - - return uniqBy( - getDataSourceSrv() - .getList({ - alerting, - tracing, - metrics, - logs, - dashboard, - mixed, - variables, - annotations, - pluginId, - filter, - type, - }) - .map((ds) => { - if (ds.type === 'datasource') { - return { - value: ds.type, - label: ds.type, - imgUrl: ds.meta.info.logos.small, - meta: ds.meta, - }; - } - - return { - value: ds.type, - label: ds.type, - imgUrl: ds.meta.info.logos.small, - meta: ds.meta, - }; - }), - (opt) => opt.value - ); -}; - -export const DatasourceTypePicker = (props: DatasourceTypePickerProps) => { - const { autoFocus, onBlur, onChange, current, openMenuOnFocus, placeholder, width, inputId } = props; - const options = getDataSourceTypeOptions(props); - - return ( -
- - {validationError && {validationError}} - - )} -
- - ); -}; - -const getStyles = (theme: GrafanaTheme2) => { - return { - wrapper: css` - display: flex; - align-items: center; - margin-left: ${theme.v1.spacing.xs}; - `, - nameEditIcon: css` - cursor: pointer; - color: ${theme.colors.text.secondary}; - width: 12px; - height: 12px; - `, - nameInput: css` - max-width: 300px; - margin: -8px 0; - `, - h2Style: css` - margin-bottom: 0; - `, - }; -}; diff --git a/public/app/features/query-library/components/SaveQueryWorkflowModal.tsx b/public/app/features/query-library/components/SaveQueryWorkflowModal.tsx deleted file mode 100644 index 674376f9991..00000000000 --- a/public/app/features/query-library/components/SaveQueryWorkflowModal.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { css } from '@emotion/css'; -import React, { useState } from 'react'; - -import { selectors } from '@grafana/e2e-selectors'; -import { Button, Form, Modal, VerticalGroup, TextArea } from '@grafana/ui'; - -import { WorkflowID } from '../../storage/types'; -import { SavedQuery } from '../api/SavedQueriesApi'; - -interface FormDTO { - message: string; -} - -export interface SaveQueryOptions { - savedQuery: SavedQuery; - workflow: WorkflowID; - message?: string; -} - -export type SaveProps = { - onCancel: () => void; - onSuccess: () => void; - onSubmit?: (options: SaveQueryOptions) => Promise<{ success: boolean }>; - options: SaveQueryOptions; - onOptionsChange: (opts: SaveQueryOptions) => void; -}; - -export const SaveQueryWorkflowModal = ({ options, onSubmit, onCancel, onSuccess }: SaveProps) => { - const [saving, setSaving] = useState(false); - - return ( - -
{ - console.log('hello submitting!'); - if (!onSubmit) { - return; - } - setSaving(true); - options = { ...options, message: data.message }; - const result = await onSubmit(options); - if (result.success) { - onSuccess(); - } else { - setSaving(false); - } - }} - > - {({ register, errors }) => ( - -