This commit is contained in:
Kristina Durivage
2024-07-01 18:41:20 -05:00
parent 78f9c0fd41
commit 77c3be054d
7 changed files with 237 additions and 13 deletions
@@ -0,0 +1,176 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { AppEvents, dateTime } from '@grafana/data';
import { DataSourcePicker, getAppEvents, getDataSourceSrv } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
import { Button, InlineSwitch, Modal, RadioButtonGroup, TextArea } from '@grafana/ui';
import { Field } from '@grafana/ui/';
import { Input } from '@grafana/ui/src/components/Input/Input';
import { Trans, t } from 'app/core/internationalization';
import { getQueryDisplayText } from 'app/core/utils/richHistory';
import { useAddQueryTemplateMutation, useEditQueryTemplateMutation } from 'app/features/query-library';
import { AddQueryTemplateCommand, EditQueryTemplateCommand } from 'app/features/query-library/types';
import { useDatasource } from '../QueryLibrary/utils/useDatasource';
import { QueryTemplateRow } from './QueryTemplatesTable/types';
type Props = {
onCancel: () => void;
onSave: (isSuccess: boolean) => void;
queryToAdd?: DataQuery;
templateData?: QueryTemplateRow;
};
export type QueryDetails = {
description: string;
};
const VisibilityOptions = [
{ value: 'Public', label: t('explore.query-library.public', 'Public') },
{ value: 'Private', label: t('explore.query-library.private', 'Private') },
];
const info = t(
'explore.add-to-library-modal.info',
`You're about to save this query. Once saved, you can easily access it in the Query Library tab for future use and reference.`
);
export const QueryTemplateForm = ({ onCancel, onSave, queryToAdd, templateData }: Props) => {
const { register, handleSubmit } = useForm<QueryDetails>({
defaultValues: {
description: templateData?.description,
},
});
const [addQueryTemplate] = useAddQueryTemplateMutation();
const [editQueryTemplate] = useEditQueryTemplateMutation();
const datasource = useDatasource(queryToAdd?.datasource);
const [queryStrings, setQueryStrings] = useState<string[]>([]);
// this is an array to support multi query templates sometime in the future
const queries =
queryToAdd !== undefined ? [queryToAdd] : templateData?.query !== undefined ? [templateData?.query] : [];
const handleAddQueryTemplate = async (addQueryTemplateCommand: AddQueryTemplateCommand) => {
return addQueryTemplate(addQueryTemplateCommand)
.unwrap()
.then(() => {
getAppEvents().publish({
type: AppEvents.alertSuccess.name,
payload: [
t('explore.query-library.query-template-added', 'Query template successfully added to the library'),
],
});
return true;
})
.catch(() => {
getAppEvents().publish({
type: AppEvents.alertError.name,
payload: [
t('explore.query-library.query-template-error', 'Error attempting to add this query to the library'),
],
});
return false;
});
};
const handleEditQueryTemplate = async (EditQueryTemplateCommand: EditQueryTemplateCommand) => {
return editQueryTemplate(EditQueryTemplateCommand)
.unwrap()
.then(() => {
getAppEvents().publish({
type: AppEvents.alertSuccess.name,
payload: [
t('explore.query-library.query-template-added', 'Query template successfully added to the library'),
],
});
return true;
})
.catch(() => {
getAppEvents().publish({
type: AppEvents.alertError.name,
payload: [
t('explore.query-library.query-template-error', 'Error attempting to add this query to the library'),
],
});
return false;
});
};
const onSubmit = async (data: QueryDetails) => {
const timestamp = dateTime().toISOString();
const temporaryDefaultTitle =
data.description || t('explore.query-library.default-description', 'Public', { timestamp: timestamp });
if (templateData?.uid) {
handleEditQueryTemplate({
uid: templateData.uid,
title: temporaryDefaultTitle,
targets: [templateData.query!],
}).then((isSuccess) => {
onSave(isSuccess);
});
} else if (queryToAdd) {
handleAddQueryTemplate({ title: temporaryDefaultTitle, targets: [queryToAdd] }).then((isSuccess) => {
onSave(isSuccess);
});
}
};
const generateQueryText = (queries: DataQuery[]) => {
const promises = queries.map(async (query, i) => {
const datasource = await getDataSourceSrv().get(query.datasource);
return datasource?.getQueryDisplayText?.(query) || getQueryDisplayText(query);
});
Promise.all(promises).then((qStrings) => setQueryStrings(qStrings));
};
generateQueryText(queries);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<p>{info}</p>
{queryStrings.map((queryString, i) => (
<Field key={`query-${i}`} label={t('explore.add-to-library-modal.query', 'Query')}>
<TextArea readOnly={true} value={queryString}></TextArea>
</Field>
))}
{queryToAdd && (
<>
<Field label={t('explore.add-to-library-modal.data-source-name', 'Data source name')}>
<DataSourcePicker current={datasource?.uid} disabled={true} />
</Field>
<Field label={t('explore.add-to-library-modal.data-source-type', 'Data source type')}>
<Input disabled={true} defaultValue={datasource?.meta.name}></Input>
</Field>
</>
)}
<Field label={t('explore.add-to-library-modal.description', 'Description')}>
<Input id="query-template-description" autoFocus={true} {...register('description')}></Input>
</Field>
<Field label={t('explore.add-to-library-modal.visibility', 'Visibility')}>
<RadioButtonGroup options={VisibilityOptions} value={'Public'} disabled={true} />
</Field>
<InlineSwitch
showLabel={true}
disabled={true}
label={t(
'explore.add-to-library-modal.auto-star',
'Auto-star this query to add it to your starred list in the Query Library.'
)}
/>
<Modal.ButtonRow>
<Button variant="secondary" onClick={() => onCancel()} fill="outline">
<Trans i18nKey="explore.query-library.cancel">Cancel</Trans>
</Button>
<Button variant="primary" type="submit">
<Trans i18nKey="explore.query-library.save">Save</Trans>
</Button>
</Modal.ButtonRow>
</form>
);
};
@@ -1,6 +1,8 @@
import { useState } from 'react';
import { reportInteraction, getAppEvents } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
import { IconButton } from '@grafana/ui';
import { IconButton, Modal } from '@grafana/ui';
import { notifyApp } from 'app/core/actions';
import { createSuccessNotification } from 'app/core/copy/appNotification';
import { t } from 'app/core/internationalization';
@@ -9,17 +11,21 @@ import { dispatch } from 'app/store/store';
import { ShowConfirmModalEvent } from 'app/types/events';
import ExploreRunQueryButton from '../../ExploreRunQueryButton';
import { QueryTemplateForm } from '../QueryTemplateForm';
import { useQueryLibraryListStyles } from './styles';
import { QueryTemplateRow } from './types';
interface ActionsCellProps {
queryUid?: string;
query?: DataQuery;
queryTemplate: QueryTemplateRow;
rootDatasourceUid?: string;
}
function ActionsCell({ query, rootDatasourceUid, queryUid }: ActionsCellProps) {
function ActionsCell({ queryTemplate, rootDatasourceUid, queryUid }: ActionsCellProps) {
const [deleteQueryTemplate] = useDeleteQueryTemplateMutation();
const [editFormOpen, setEditFormOpen] = useState(false);
//const [editQueryTemplate] = useEditQueryTemplateMutation();
const styles = useQueryLibraryListStyles();
const onDeleteQuery = (queryUid: string) => {
@@ -57,7 +63,33 @@ function ActionsCell({ query, rootDatasourceUid, queryUid }: ActionsCellProps) {
}
}}
/>
<ExploreRunQueryButton queries={query ? [query] : []} rootDatasourceUid={rootDatasourceUid} />
<IconButton
className={styles.actionButton}
size="lg"
name="comment-alt"
title={t('explore.query-library.delete-query', 'Add/edit description')}
tooltip={t('explore.query-library.delete-query', 'Add/edit description')}
onClick={() => {
setEditFormOpen(true);
}}
/>
<ExploreRunQueryButton
queries={queryTemplate.query ? [queryTemplate.query] : []}
rootDatasourceUid={rootDatasourceUid}
/>
<Modal
title={t('explore.add-to-library-modal.title', 'Add query to Query Library')}
isOpen={editFormOpen}
onDismiss={() => setEditFormOpen(false)}
>
<QueryTemplateForm
onCancel={() => setEditFormOpen(false)}
templateData={queryTemplate}
onSave={(data) => {
console.log(data);
}}
/>
</Modal>
</div>
);
}
@@ -25,7 +25,7 @@ const columns: Array<Column<QueryTemplateRow>> = [
id: 'actions',
header: '',
cell: ({ row: { original } }) => (
<ActionsCell query={original.query} rootDatasourceUid={original.datasourceRef?.uid} queryUid={original.uid} />
<ActionsCell queryTemplate={original} rootDatasourceUid={original.datasourceRef?.uid} queryUid={original.uid} />
),
},
];
@@ -1,8 +1,8 @@
import { createApi } from '@reduxjs/toolkit/query/react';
import { AddQueryTemplateCommand, DeleteQueryTemplateCommand, QueryTemplate } from '../types';
import { AddQueryTemplateCommand, DeleteQueryTemplateCommand, QueryTemplate, EditQueryTemplateCommand } from '../types';
import { convertAddQueryTemplateCommandToDataQuerySpec, convertDataQueryResponseToQueryTemplates } from './mappers';
import { convertQueryTemplateCommandToDataQuerySpec, convertDataQueryResponseToQueryTemplates } from './mappers';
import { baseQuery } from './query';
export const queryLibraryApi = createApi({
@@ -17,7 +17,7 @@ export const queryLibraryApi = createApi({
addQueryTemplate: builder.mutation<QueryTemplate, AddQueryTemplateCommand>({
query: (addQueryTemplateCommand) => ({
method: 'POST',
data: convertAddQueryTemplateCommandToDataQuerySpec(addQueryTemplateCommand),
data: convertQueryTemplateCommandToDataQuerySpec(addQueryTemplateCommand),
}),
invalidatesTags: ['QueryTemplatesList'],
}),
@@ -28,6 +28,14 @@ export const queryLibraryApi = createApi({
}),
invalidatesTags: ['QueryTemplatesList'],
}),
editQueryTemplate: builder.mutation<void, EditQueryTemplateCommand>({
query: (editQueryTemplateCommand) => ({
url: `${editQueryTemplateCommand.uid}`,
method: 'POST',
data: convertQueryTemplateCommandToDataQuerySpec(editQueryTemplateCommand),
}),
invalidatesTags: ['QueryTemplatesList'],
}),
}),
reducerPath: 'queryLibrary',
});
@@ -1,4 +1,4 @@
import { AddQueryTemplateCommand, QueryTemplate } from '../types';
import { AddQueryTemplateCommand, EditQueryTemplateCommand, QueryTemplate } from '../types';
import { API_VERSION, QueryTemplateKinds } from './query';
import { CREATED_BY_KEY, DataQuerySpec, DataQuerySpecResponse, DataQueryTarget } from './types';
@@ -40,8 +40,8 @@ export const convertDataQueryResponseToQueryTemplates = (result: DataQuerySpecRe
});
};
export const convertAddQueryTemplateCommandToDataQuerySpec = (
addQueryTemplateCommand: AddQueryTemplateCommand
export const convertQueryTemplateCommandToDataQuerySpec = (
addQueryTemplateCommand: AddQueryTemplateCommand | EditQueryTemplateCommand
): DataQuerySpec => {
const { title, targets } = addQueryTemplateCommand;
return {
+6 -2
View File
@@ -12,8 +12,12 @@ import { config } from '@grafana/runtime';
import { queryLibraryApi } from './api/factory';
import { mockData } from './api/mocks';
export const { useAllQueryTemplatesQuery, useAddQueryTemplateMutation, useDeleteQueryTemplateMutation } =
queryLibraryApi;
export const {
useAllQueryTemplatesQuery,
useAddQueryTemplateMutation,
useDeleteQueryTemplateMutation,
useEditQueryTemplateMutation,
} = queryLibraryApi;
export function isQueryLibraryEnabled() {
return config.featureToggles.queryLibrary;
@@ -13,6 +13,10 @@ export type AddQueryTemplateCommand = {
targets: DataQuery[];
};
export interface EditQueryTemplateCommand extends AddQueryTemplateCommand {
uid: string;
}
export type DeleteQueryTemplateCommand = {
uid: string;
};