WIP
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user