Browse Source

Merge pull request 'subscribe-to-events' (#4) from subscribe-to-events into master

Reviewed-on: #4
pull/6/head
rozetko 2 years ago
parent
commit
2a3ca8e089
  1. 202
      src/panels/corpglory-dataexporter-panel/components/Panel.tsx
  2. 12
      src/panels/corpglory-dataexporter-panel/types.ts

202
src/panels/corpglory-dataexporter-panel/components/Panel.tsx

@ -1,8 +1,9 @@
import { PanelOptions, TableRowConfig } from '../types';
import { PanelOptions, TaskTableRowConfig, DatasourceTableRowConfig } from '../types';
import { getBackendSrv } from '@grafana/runtime';
import { makeRequest } from 'services/network_service';
import { Table, Button, HorizontalGroup, LoadingPlaceholder } from '@grafana/ui';
import { Table, Button, HorizontalGroup, Modal, LoadingPlaceholder } from '@grafana/ui';
import {
PanelProps,
toDataFrame,
@ -19,61 +20,133 @@ import * as _ from 'lodash';
interface Props extends PanelProps<PanelOptions> {}
export function Panel({ options, data, width, height, timeRange, onChangeTimeRange }: Props) {
console.log('panel', data);
const [tasks, setTasks] = useState<TableRowConfig[] | null>(null);
const [dataFrame, setDataFrame] = useState<DataFrame | null>(null);
const [tasks, setTasks] = useState<TaskTableRowConfig[] | null>(null);
const [tasksDataFrame, setTasksDataFrame] = useState<DataFrame | null>(null);
useEffect(() => {
if (tasks === null) {
return;
}
const dataFrame = getDataFrameForTable(tasks, setTasks);
setDataFrame(dataFrame)
const dataFrame = getDataFrameForTaskTable(tasks, setTasks);
setTasksDataFrame(dataFrame);
}, [tasks]);
useEffect(() => {
// TODO: move this function to Network Service
async function getTasks(): Promise<TableRowConfig[]> {
return makeRequest<TableRowConfig[]>('/tasks', {});
async function getTasks(): Promise<TaskTableRowConfig[]> {
return makeRequest<TaskTableRowConfig[]>('/tasks', {});
}
getTasks().then(tasks => setTasks(tasks)).catch(err => console.error(err));
getTasks()
.then((tasks) => setTasks(tasks))
.catch((err) => console.error(err));
}, []);
function onAddTaskClick(): void {
if(tasks === null) {
const datasourceConfigs: DatasourceTableRowConfig[] = [];
const [datasources, setDatasources] = useState(datasourceConfigs);
const datasourceDataFrame = getDataFrameForDatasourceTable(datasources, setDatasources);
const [isModalOpen, setModalVisibility] = useState(false);
const timestampRange: [number, number] = [timeRange.from.unix(), timeRange.to.unix()];
const backendSrv = getBackendSrv();
// @ts-ignore
backendSrv.getInspectorStream().subscribe({
next: (resp: any) => {
const queries = resp?.config?.data?.queries;
if (_.isEmpty(queries)) {
return;
}
const configs = [...tasks, createConfigItem()];
setTasks(configs);
const datasource = queries[0].datasource.type;
const refId = queries[0].refId;
const rawSql = queries[0].rawSql;
const uid = queries[0].datasource.uid;
// TODO: it works only with sql (rawSql will be empty in prometheus)
const isDatasourceExist = _.some(
datasources,
(ds: DatasourceTableRowConfig) =>
ds.datasource === datasource && ds.refId === refId && _.isEqual(ds.rawSql, rawSql) && ds.uid === uid
);
if (isDatasourceExist) {
return;
}
const newConfig = createDatasourceConfig({ datasource, refId, rawSql, uid }, timestampRange);
setDatasources([...datasources, newConfig]);
},
});
function onAddTaskClick(): void {
const selectedDatasources = _.filter(datasources, (datasource: DatasourceTableRowConfig) => datasource.select);
const newTasks = _.map(selectedDatasources, (datasource: DatasourceTableRowConfig) =>
createTaskFromDatasource(datasource)
);
if (_.isEmpty(tasks)) {
setTasks([...newTasks]);
} else {
setTasks([...(tasks as TaskTableRowConfig[]), ...newTasks]);
}
onCloseModal();
unselectAllDatasources();
}
function unselectAllDatasources(): void {
const updatedDatasources = _.clone(datasources);
for (const ds of updatedDatasources) {
ds.select = false;
}
setDatasources(updatedDatasources);
}
function openDatasourceModal(): void {
setModalVisibility(true);
}
function onCloseModal(): void {
setModalVisibility(false);
unselectAllDatasources();
}
return (
<div>
{dataFrame === null ?
{tasksDataFrame === null ? (
// TODO: if datasource responds with error, display the error
<LoadingPlaceholder text="Loading..."></LoadingPlaceholder> :
<LoadingPlaceholder text="Loading..."></LoadingPlaceholder>
) : (
<div>
<Table width={width} height={height - 40} data={dataFrame} />
<Table width={width} height={height - 40} data={tasksDataFrame} />
<HorizontalGroup justify="flex-end">
<Button
variant="primary"
aria-label="Rich history button"
icon="plus"
style={{ marginTop: '8px' }}
onClick={openDatasourceModal}
>
Add Task
</Button>
<Modal title="Select Datasources" isOpen={isModalOpen} onDismiss={onCloseModal}>
<Table width={width / 2 - 20} height={height - 40} data={datasourceDataFrame} />
<HorizontalGroup justify="flex-end">
<Button
variant="primary"
aria-label="Add task button"
style={{ marginTop: '8px' }}
onClick={onAddTaskClick}
>
Add Task
</Button>
</HorizontalGroup>
</Modal>
</HorizontalGroup>
</div>
}
)}
</div>
);
}
function getDataFrameForTable(configs: TableRowConfig[], setTasks: any): DataFrame {
function getDataFrameForTaskTable(configs: TaskTableRowConfig[], setTasks: any): DataFrame {
const dataFrame = toDataFrame({
name: 'A',
fields: [
@ -159,23 +232,102 @@ function getDataFrameForTable(configs: TableRowConfig[], setTasks: any): DataFra
return dataFrames[0];
}
function onDeleteClick(e: DataLinkClickEvent, tasks: TableRowConfig[], setTasks: any): void {
function getDataFrameForDatasourceTable(configs: DatasourceTableRowConfig[], setDatasources: any): DataFrame {
const dataFrame = toDataFrame({
name: 'A',
fields: [
{
name: 'Select',
type: FieldType.string,
values: _.map(
configs,
(config) => `data:image/svg+xml;base64,${config.select ? SELECT_ICON_BASE_64 : UNSELECT_ICON_BASE_64}`
),
config: {
custom: {
filterable: false,
displayMode: 'image',
},
links: [
{
targetBlank: false,
title: 'Select',
url: '#',
onClick: (event: DataLinkClickEvent) => onDatasourceSelectClick(event, configs, setDatasources),
},
],
},
},
{
name: 'Panel',
type: FieldType.string,
values: _.map(configs, (config) => config.panel),
},
{
name: 'RefId',
type: FieldType.string,
values: _.map(configs, (config) => config.refId),
},
{
name: 'Datasource',
type: FieldType.string,
values: _.map(configs, (config) => config.datasource),
},
],
});
const dataFrames = applyFieldOverrides({
data: [dataFrame],
fieldConfig: {
overrides: [],
defaults: {},
},
theme: createTheme(),
replaceVariables: (value: string) => value,
});
return dataFrames[0];
}
function onDeleteClick(e: DataLinkClickEvent, tasks: TaskTableRowConfig[], setTasks: any): void {
const rowIndex = e.origin.rowIndex;
const filteredTasks = _.filter(tasks, (task, idx) => idx !== rowIndex);
setTasks(filteredTasks);
}
function createConfigItem(): TableRowConfig {
function onDatasourceSelectClick(
e: DataLinkClickEvent,
datasources: DatasourceTableRowConfig[],
setDatasources: any
): void {
const rowIndex = e.origin.rowIndex;
const updatedDatasources = _.clone(datasources);
updatedDatasources[rowIndex].select = !updatedDatasources[rowIndex].select;
setDatasources(updatedDatasources);
}
function createTaskFromDatasource(config: DatasourceTableRowConfig): TaskTableRowConfig {
return {
timestamp: Date.now(),
user: 'admin',
datasource: 'Prometheus',
datasource: config.datasource,
rowsCount: 3214,
progress: 100,
status: 'finished',
};
}
function createDatasourceConfig(obj: any, timerange: [number, number]): DatasourceTableRowConfig {
return {
select: false,
panel: 'Panel',
refId: obj.refId,
datasource: obj.datasource,
rawSql: obj.rawSql,
uid: obj.uid,
timerange,
};
}
function convertTimestampToDate(timestamp: number): string {
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
@ -192,3 +344,7 @@ const CLOSE_ICON_BASE_64 =
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACJSURBVHgB7ZPRCcAgDESv0lWc0zhC53OIjmAVUpCSamL76YEoyfFIiAGWfldK6SwnKHyhep9xJ3iPcqgH5RyxV1VlBWYJypXVHMEiCaqBbRhAy3W3B76j954wq6ZSVZsOY+WXt6i9l2ymGTlUq0VpOcIqaQC96Zth01DN1zBBefVI4SNp9Za+6wLcH6DKFrfpxgAAAABJRU5ErkJggg==';
const DOWNLOAD_ICON_BASE_64 =
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADlSURBVHgB3ZPNDYJAEIVnN96lBA7A2RIsATuQiiwBSrAD7MAjCZBACXqFwPomMRtEwMVwgZeQhfn5mNnMEG1CaZoeiqKwTWJ3JkFCiEtVVU+8+r9iJRlKSrk3iqOFtWJglmXHf3yDwCRJbBxxnudh34cRYluMMbKGcgWNCIlnjEuIJ1JK2WzDWeKb7YHjONEsYBf6kTABY+mWuYX+3Xiex9UFU7D3FllfwLqueQvi/h8ZCtBprDLY703T6A0yWj2ArmSoxedQV4jSSz5xj4pmCi0/NKfrwNz5bdtaNENciOu6N1qNXhzZXHMb9Q+nAAAAAElFTkSuQmCC';
const UNSELECT_ICON_BASE_64 =
'PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgcng9IjEuNSIgZmlsbD0iIzExMTIxNiIgc3Ryb2tlPSIjMkQyRTM0Ii8+Cjwvc3ZnPgo=';
const SELECT_ICON_BASE_64 =
'PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiByeD0iMiIgZmlsbD0iIzRBNzFEMiIvPgo8cGF0aCBkPSJNNS45ODI2NCAxMi41MjE3QzUuNzczOTUgMTIuNTIxNyA1LjU4MjY0IDEyLjQ1MjIgNS40MjYxMiAxMi4yOTU2TDEuOTY1MjUgOC44MzQ3NkMxLjY1MjIxIDguNTIxNzIgMS42NTIyMSA4LjAzNDc2IDEuOTY1MjUgNy43MjE3MkMyLjI3ODI5IDcuNDA4NjcgMi43NjUyNSA3LjQwODY3IDMuMDc4MjkgNy43MjE3Mkw2LjAwMDAzIDEwLjYyNjFMMTIuOTM5MiAzLjcwNDMzQzEzLjI1MjIgMy4zOTEyOCAxMy43MzkyIDMuMzkxMjggMTQuMDUyMiAzLjcwNDMzQzE0LjM2NTMgNC4wMTczNyAxNC4zNjUzIDQuNTA0MzMgMTQuMDUyMiA0LjgxNzM3TDYuNTU2NTYgMTIuMjk1NkM2LjM4MjY0IDEyLjQ1MjIgNi4xOTEzNCAxMi41MjE3IDUuOTgyNjQgMTIuNTIxN1YxMi41MjE3WiIgZmlsbD0iI0ZFRkZGRiIvPgo8L3N2Zz4K';

12
src/panels/corpglory-dataexporter-panel/types.ts

@ -1,6 +1,6 @@
export interface PanelOptions {}
export type TableRowConfig = {
export type TaskTableRowConfig = {
timestamp: number;
user: string;
datasource: string;
@ -8,3 +8,13 @@ export type TableRowConfig = {
progress: number;
status: string;
};
export type DatasourceTableRowConfig = {
select: boolean;
panel: string;
refId: string;
datasource: string;
rawSql: string;
uid: string;
timerange: [number, number];
};

Loading…
Cancel
Save