diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx
index a1e8bb5c568..6989044a9c4 100644
--- a/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx
+++ b/public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx
@@ -41,6 +41,7 @@ export function QueryTemplatesList() {
const datasourceType = getDatasourceSrv().getInstanceSettings(datasourceRef)?.meta.name || '';
return {
index: index.toString(),
+ uid: queryTemplate.uid,
datasourceRef,
datasourceType,
createdAtTimestamp: queryTemplate?.createdAtTimestamp || 0,
diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx
index 6dd1c181d69..4ffd14edad6 100644
--- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx
+++ b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/ActionsCell.tsx
@@ -1,16 +1,67 @@
import React from 'react';
+import { reportInteraction, getAppEvents } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
+import { IconButton } from '@grafana/ui';
+import { notifyApp } from 'app/core/actions';
+import { createSuccessNotification } from 'app/core/copy/appNotification';
+import { t } from 'app/core/internationalization';
+import { useDeleteQueryTemplateMutation } from 'app/features/query-library';
+import { dispatch } from 'app/store/store';
+import { ShowConfirmModalEvent } from 'app/types/events';
import ExploreRunQueryButton from '../../ExploreRunQueryButton';
+import { useQueryLibraryListStyles } from './styles';
+
interface ActionsCellProps {
+ queryUid?: string;
query?: DataQuery;
rootDatasourceUid?: string;
}
-function ActionsCell({ query, rootDatasourceUid }: ActionsCellProps) {
- return ;
+function ActionsCell({ query, rootDatasourceUid, queryUid }: ActionsCellProps) {
+ const [deleteQueryTemplate] = useDeleteQueryTemplateMutation();
+ const styles = useQueryLibraryListStyles();
+
+ const onDeleteQuery = (queryUid: string) => {
+ const performDelete = (queryUid: string) => {
+ deleteQueryTemplate({ uid: queryUid });
+ dispatch(notifyApp(createSuccessNotification(t('explore.query-library.query-deleted', 'Query deleted'))));
+ reportInteraction('grafana_explore_query_library_deleted');
+ };
+
+ getAppEvents().publish(
+ new ShowConfirmModalEvent({
+ title: t('explore.query-library.delete-query-title', 'Delete query'),
+ text: t(
+ 'explore.query-library.delete-query-text',
+ "You're about to remove this query from the query library. This action cannot be undone. Do you want to continue?"
+ ),
+ yesText: t('query-library.delete-query-button', 'Delete query'),
+ icon: 'trash-alt',
+ onConfirm: () => performDelete(queryUid),
+ })
+ );
+ };
+
+ return (
+
+ {
+ if (queryUid) {
+ onDeleteQuery(queryUid);
+ }
+ }}
+ />
+
+
+ );
}
export default ActionsCell;
diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx
index 56625e3cf15..0b90f8d29dd 100644
--- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx
+++ b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/index.tsx
@@ -26,7 +26,7 @@ const columns: Array> = [
id: 'actions',
header: '',
cell: ({ row: { original } }) => (
-
+
),
},
];
diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx
index 1c7897e6e5d..715cd5d235b 100644
--- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx
+++ b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/styles.tsx
@@ -34,4 +34,14 @@ const getStyles = (theme: GrafanaTheme2) => ({
WebkitLineClamp: 1,
overflow: 'hidden',
}),
+ cell: css({
+ display: 'flex',
+ alignItems: 'center',
+ '&:last-child': {
+ justifyContent: 'end',
+ },
+ }),
+ actionButton: css({
+ padding: theme.spacing(1),
+ }),
});
diff --git a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts
index 62ddcf12420..59cc7b24b6a 100644
--- a/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts
+++ b/public/app/features/explore/QueryLibrary/QueryTemplatesTable/types.ts
@@ -7,4 +7,5 @@ export type QueryTemplateRow = {
datasourceRef?: DataSourceRef | null;
datasourceType?: string;
createdAtTimestamp?: number;
+ uid?: string;
};
diff --git a/public/app/features/query-library/api/factory.ts b/public/app/features/query-library/api/factory.ts
index bff1973010f..09cc17f76f6 100644
--- a/public/app/features/query-library/api/factory.ts
+++ b/public/app/features/query-library/api/factory.ts
@@ -1,6 +1,6 @@
import { createApi } from '@reduxjs/toolkit/query/react';
-import { AddQueryTemplateCommand, QueryTemplate } from '../types';
+import { AddQueryTemplateCommand, DeleteQueryTemplateCommand, QueryTemplate } from '../types';
import { convertAddQueryTemplateCommandToDataQuerySpec, convertDataQueryResponseToQueryTemplates } from './mappers';
import { baseQuery } from './query';
@@ -21,6 +21,13 @@ export const queryLibraryApi = createApi({
}),
invalidatesTags: ['QueryTemplatesList'],
}),
+ deleteQueryTemplate: builder.mutation({
+ query: ({ uid }) => ({
+ url: `${uid}`,
+ method: 'DELETE',
+ }),
+ invalidatesTags: ['QueryTemplatesList'],
+ }),
}),
reducerPath: 'queryLibrary',
});
diff --git a/public/app/features/query-library/api/query.ts b/public/app/features/query-library/api/query.ts
index 80464e1625b..b3baced6237 100644
--- a/public/app/features/query-library/api/query.ts
+++ b/public/app/features/query-library/api/query.ts
@@ -24,15 +24,20 @@ export enum QueryTemplateKinds {
*/
export const BASE_URL = `/apis/${API_VERSION}/namespaces/default/querytemplates/`;
+// URL is optional for these requests
+interface QueryLibraryBackendRequest extends Pick {
+ url?: string;
+}
+
/**
* TODO: similar code is duplicated in many places. To be unified in #86960
*/
-export const baseQuery: BaseQueryFn, DataQuerySpecResponse, Error> = async (
+export const baseQuery: BaseQueryFn = async (
requestOptions
) => {
try {
const responseObservable = getBackendSrv().fetch({
- url: BASE_URL,
+ url: `${BASE_URL}${requestOptions.url ?? ''}`,
showErrorAlert: true,
method: requestOptions.method || 'GET',
data: requestOptions.data,
diff --git a/public/app/features/query-library/index.ts b/public/app/features/query-library/index.ts
index 3c2e74e1f81..513b7ca5bb8 100644
--- a/public/app/features/query-library/index.ts
+++ b/public/app/features/query-library/index.ts
@@ -12,7 +12,8 @@ import { config } from '@grafana/runtime';
import { queryLibraryApi } from './api/factory';
import { mockData } from './api/mocks';
-export const { useAllQueryTemplatesQuery, useAddQueryTemplateMutation } = queryLibraryApi;
+export const { useAllQueryTemplatesQuery, useAddQueryTemplateMutation, useDeleteQueryTemplateMutation } =
+ queryLibraryApi;
export function isQueryLibraryEnabled() {
return config.featureToggles.queryLibrary;
diff --git a/public/app/features/query-library/types.ts b/public/app/features/query-library/types.ts
index 030d292c901..9be092869d4 100644
--- a/public/app/features/query-library/types.ts
+++ b/public/app/features/query-library/types.ts
@@ -11,3 +11,7 @@ export type AddQueryTemplateCommand = {
title: string;
targets: DataQuery[];
};
+
+export type DeleteQueryTemplateCommand = {
+ uid: string;
+};
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index 2f81df6b661..66cd0278d59 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -532,6 +532,12 @@
"scan-for-older-logs": "Scan for older logs",
"stop-scan": "Stop scan"
},
+ "query-library": {
+ "delete-query": "Delete query",
+ "delete-query-text": "You're about to remove this query from the query library. This action cannot be undone. Do you want to continue?",
+ "delete-query-title": "Delete query",
+ "query-deleted": "Query deleted"
+ },
"rich-history": {
"close-tooltip": "Close query history",
"datasource-a-z": "Data source A-Z",
@@ -1559,6 +1565,9 @@
"role-label": "Role"
}
},
+ "query-library": {
+ "delete-query-button": "Delete query"
+ },
"query-operation": {
"header": {
"collapse-row": "Collapse query row",
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index 288b9f25abc..ce0e02527a6 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -532,6 +532,12 @@
"scan-for-older-logs": "Ŝčäʼn ƒőř őľđęř ľőģş",
"stop-scan": "Ŝŧőp şčäʼn"
},
+ "query-library": {
+ "delete-query": "Đęľęŧę qūęřy",
+ "delete-query-text": "Ÿőū'řę äþőūŧ ŧő řęmővę ŧĥįş qūęřy ƒřőm ŧĥę qūęřy ľįþřäřy. Ŧĥįş äčŧįőʼn čäʼnʼnőŧ þę ūʼnđőʼnę. Đő yőū ŵäʼnŧ ŧő čőʼnŧįʼnūę?",
+ "delete-query-title": "Đęľęŧę qūęřy",
+ "query-deleted": "Qūęřy đęľęŧęđ"
+ },
"rich-history": {
"close-tooltip": "Cľőşę qūęřy ĥįşŧőřy",
"datasource-a-z": "Đäŧä şőūřčę Å-Ż",
@@ -1559,6 +1565,9 @@
"role-label": "Ŗőľę"
}
},
+ "query-library": {
+ "delete-query-button": "Đęľęŧę qūęřy"
+ },
"query-operation": {
"header": {
"collapse-row": "Cőľľäpşę qūęřy řőŵ",