From be457f63b2033b05af58251a06ad77c130ca955a Mon Sep 17 00:00:00 2001 From: sumukhswamy Date: Wed, 25 Oct 2023 17:49:21 -0700 Subject: [PATCH] UI-bug fixes, added create query for MV (#182) * fixed MV load bug, added mv create button, handled error case when post query async fails Signed-off-by: sumukhswamy * addressed pr commits Signed-off-by: sumukhswamy * changed the materialiazed view check Signed-off-by: sumukhswamy * changed badge to notif badge for mv Signed-off-by: sumukhswamy * type check for mv Signed-off-by: sumukhswamy * added label for mv, changed mv condition Signed-off-by: sumukhswamy --------- Signed-off-by: sumukhswamy --- common/constants/index.ts | 25 +- common/utils/async_query_helpers.ts | 3 + public/components/SQLPage/CreateButton.tsx | 19 +- public/components/SQLPage/table_view.tsx | 356 ++++++++++++++------- 4 files changed, 286 insertions(+), 117 deletions(-) diff --git a/common/constants/index.ts b/common/constants/index.ts index e8a1b68f..2f04ad9d 100644 --- a/common/constants/index.ts +++ b/common/constants/index.ts @@ -18,18 +18,20 @@ export const TREE_ITEM_TABLE_NAME_DEFAULT_NAME = `table`; export const TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME = `Load Materialized View`; export const TREE_ITEM_BADGE_NAME = `badge`; export const LOAD_OPENSEARCH_INDICES_QUERY = `SHOW tables LIKE '%';`; -export const SKIPPING_INDEX_QUERY = `CREATE SKIPPING INDEX ON myS3.logs_db.http_logs +export const SKIPPING_INDEX_QUERY = `CREATE SKIPPING INDEX ON datasource.database.table (status VALUE_SET) WITH ( - auto_refresh = true + auto_refresh = true, + checkpoint_location = 's3://test/' )`; -export const COVERING_INDEX_QUERY = `CREATE INDEX covering_idx ON myS3.logs_db.http_logs +export const COVERING_INDEX_QUERY = `CREATE INDEX covering_idx ON datasource.database.table (status) WITH ( - auto_refresh = true + auto_refresh = true, + checkpoint_location = 's3://test/' )`; -export const CREATE_DATABASE_QUERY = `CREATE DATABASE myS3.logs_db`; -export const CREATE_TABLE_QUERY = `CREATE EXTERNAL TABLE myS3.logs_db.logs ( +export const CREATE_DATABASE_QUERY = `CREATE DATABASE datasource.database`; +export const CREATE_TABLE_QUERY = `CREATE EXTERNAL TABLE datasource.database.table ( key BIGINT, status INTEGER, size FLOAT, @@ -42,6 +44,17 @@ OPTIONS ( compression 'gzip' );`; +export const CREATE_MATERIALIZED_VIEW = `CREATE MATERIALIZED VIEW datasource.database.materialized_view +AS SELECT + count(field) +FROM datasource.database.table +GROUP BY TUMBLE (timestamp, '2 hours') + WITH ( +auto_refresh = true, +watermark_delay = '2 minutes', +checkpoint_location = 's3://test/' +)`; + export const ACCELERATION_INDEX_TYPES = [ { label: 'Skipping Index', value: 'skipping' }, { label: 'Covering Index', value: 'covering' }, diff --git a/common/utils/async_query_helpers.ts b/common/utils/async_query_helpers.ts index b2153237..bc4f874c 100644 --- a/common/utils/async_query_helpers.ts +++ b/common/utils/async_query_helpers.ts @@ -30,6 +30,9 @@ export const getJobId = (query: {}, http: CoreStart['http'], callback) => { .then((res) => { const id = res.data.resp.queryId; setAsyncSessionId(_.get(res.data.resp, 'sessionId', null)); + if (id === undefined) { + console.error(JSON.parse(res.data.body)); + } callback(id); }) .catch((err) => { diff --git a/public/components/SQLPage/CreateButton.tsx b/public/components/SQLPage/CreateButton.tsx index d22eaf34..2120b926 100644 --- a/public/components/SQLPage/CreateButton.tsx +++ b/public/components/SQLPage/CreateButton.tsx @@ -8,8 +8,9 @@ import React, { useState } from 'react'; import { COVERING_INDEX_QUERY, CREATE_DATABASE_QUERY, + CREATE_MATERIALIZED_VIEW, CREATE_TABLE_QUERY, - SKIPPING_INDEX_QUERY, + SKIPPING_INDEX_QUERY } from '../../../common/constants'; interface CreateButtonProps { @@ -57,6 +58,13 @@ export const CreateButton = ({ updateSQLQueries, selectedDatasource }: CreateBut }, ]; + const materializedViewItems = [ + { + name: 'Materialized View', + onClick: () => handleSubMenuClick(CREATE_MATERIALIZED_VIEW), + }, + ]; + const button = ( togglePopover(null)}> Create @@ -88,6 +96,10 @@ export const CreateButton = ({ updateSQLQueries, selectedDatasource }: CreateBut name: 'Acceleration Index', panel: 2, }, + { + name: 'Materialized View', + panel: 3, + } ], }, { @@ -100,6 +112,11 @@ export const CreateButton = ({ updateSQLQueries, selectedDatasource }: CreateBut title: 'Acceleration Index Options', items: acceleratedIndexItems, }, + { + id: 3, + title: 'Create Materialized View', + items: materializedViewItems, + }, ]} /> diff --git a/public/components/SQLPage/table_view.tsx b/public/components/SQLPage/table_view.tsx index bc3206ad..24377a6b 100644 --- a/public/components/SQLPage/table_view.tsx +++ b/public/components/SQLPage/table_view.tsx @@ -11,10 +11,11 @@ import { EuiFlexItem, EuiIcon, EuiLoadingSpinner, + EuiNotificationBadge, EuiSpacer, EuiText, EuiToolTip, - EuiTreeView, + EuiTreeView } from '@elastic/eui'; import { AccelerationIndexType, DatasourceTreeLoading, TreeItem, TreeItemType } from 'common/types'; import _ from 'lodash'; @@ -122,7 +123,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } flag: false, status: 'Error fetching data', }); - setToast(`ERROR: fetching data`, 'danger'); + setToast(`ERROR fetching data`, 'danger'); } }) .catch((err) => { @@ -131,7 +132,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } flag: false, status: err, }); - setToast(`ERROR: ${err}`,'danger'); + setToast(`ERROR ${err}`, 'danger'); }); } else { setTableNames([]); @@ -141,6 +142,14 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } datasource: selectedItems[0]['label'], }; getJobId(query, http, (id) => { + if (id === undefined) { + const errorMessage = 'ERROR fetching databases'; + setIsLoading({ + flag: false, + status: errorMessage, + }); + setToast(errorMessage, 'danger'); + } pollQueryStatus(id, http, (data) => { setIsLoading({ flag: true, status: data.status }); if (data.status === 'SUCCESS') { @@ -152,7 +161,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } flag: false, status: data.error, }); - setToast(`ERROR: ${data.error}`,'danger'); + setToast(`ERROR ${data.error}`, 'danger'); } }); }); @@ -183,31 +192,48 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } datasource: selectedItems[0]['label'], }; getJobId(query, http, (id) => { - pollQueryStatus(id, http, (data) => { - if (data.status === 'SUCCESS') { - const fetchTables = data.results.map((subArray) => subArray[1]); - let values = loadTreeItem(fetchTables, TREE_ITEM_TABLE_NAME_DEFAULT_NAME); - let mvObj = loadTreeItem( - [TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME], - TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME - ); - values = [...values, ...mvObj]; - setTreeData((prevTreeData) => { - return prevTreeData.map((database) => { - if (database.name === databaseName) { - return { ...database, values: values, isExpanded: true, isLoading: false }; - } - return database; - }); - }); - } else if (data.status === 'FAILED') { - setIsLoading({ - flag: false, - status: data.error, + if (id === undefined) { + const errorMessage = 'ERROR fetching Tables'; + setIsLoading({ + flag: false, + status: errorMessage, + }); + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === databaseName) { + return { ...database, isExpanded: true, isLoading: false }; + } + return database; }); - setToast(`ERROR: ${data.error}`,'danger'); - } - }); + }); + setToast(errorMessage, 'danger'); + } else { + pollQueryStatus(id, http, (data) => { + if (data.status === 'SUCCESS') { + const fetchTables = data.results.map((subArray) => subArray[1]); + let values = loadTreeItem(fetchTables, TREE_ITEM_TABLE_NAME_DEFAULT_NAME); + let mvObj = loadTreeItem( + [TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME], + TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME + ); + values = [...values, ...mvObj]; + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === databaseName) { + return { ...database, values: values, isExpanded: true, isLoading: false }; + } + return database; + }); + }); + } else if (data.status === 'FAILED') { + setIsLoading({ + flag: false, + status: data.error, + }); + setToast(`ERROR ${data.error}`, 'danger'); + } + }); + } }); }; @@ -218,6 +244,34 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } datasource: selectedItems[0]['label'], }; getJobId(coverQuery, http, (id) => { + if (id === undefined) { + const errorMessage = 'ERROR fetching Covering Index'; + setIsLoading({ + flag: false, + status: errorMessage, + }); + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === selectedDatabase) { + return { + ...database, + values: database.values?.map((table) => { + if (table.name === tableName) { + return { + ...table, + isLoading: false, + isExpanded: false, + }; + } + return table; + }), + }; + } + return database; + }); + }); + setToast(errorMessage, 'danger'); + } pollQueryStatus(id, http, (data) => { if (data.status === 'SUCCESS') { const res = [].concat(data.results); @@ -254,7 +308,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } flag: false, status: data.error, }); - setToast(`ERROR: ${data.error}`, 'danger'); + setToast(`ERROR ${data.error}`, 'danger'); } }); }); @@ -262,6 +316,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } const handleButtonClick = (e: MouseEvent, tableName: string) => { e.stopPropagation(); + tableName = TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME; setSelectedTable(tableName); setTreeData((prevTreeData) => { return prevTreeData.map((database) => { @@ -288,44 +343,75 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } datasource: selectedItems[0]['label'], }; getJobId(materializedViewQuery, http, (id) => { - pollQueryStatus(id, http, (data) => { - if (data.status === 'SUCCESS') { - const fetchMaterialzedView = data.results.map((subArray) => subArray[1]); - let values = loadTreeItem(fetchMaterialzedView, TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME); - if (values.length === 0) { - values = [ - { - name: 'No Materialized View', - type: TREE_ITEM_BADGE_NAME, - isExpanded: false, - isLoading: false, - }, - ]; - } - setTreeData((prevTreeData) => { - return prevTreeData.map((database) => { - if (database.name === selectedDatabase) { - const updatedValues = database.values?.filter( - (item) => item.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME - ); - return { - ...database, - values: updatedValues?.concat(...values), + if (id === undefined) { + const errorMessage = 'ERROR fetching Materialized View'; + setIsLoading({ + flag: false, + status: errorMessage, + }); + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === selectedDatabase) { + return { + ...database, + values: database.values?.map((table) => { + if (table.name === tableName) { + return { + ...table, + isLoading: false, + }; + } + return table; + }), + }; + } + return database; + }); + }); + setToast(errorMessage, 'danger'); + } else { + pollQueryStatus(id, http, (data) => { + if (data.status === 'SUCCESS') { + const fetchMaterialzedView = data.results; + let values = loadTreeItem( + fetchMaterialzedView, + TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME + ); + if (values.length === 0) { + values = [ + { + name: 'No Materialized View', + type: TREE_ITEM_BADGE_NAME, + isExpanded: false, isLoading: false, - isExpanded: true, - }; - } - return database; + }, + ]; + } + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === selectedDatabase) { + const updatedValues = database.values?.filter( + (item) => item.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME + ); + return { + ...database, + values: updatedValues?.concat(...values), + isLoading: false, + isExpanded: true, + }; + } + return database; + }); }); - }); - } else if (data.status === 'FAILED') { - setIsLoading({ - flag: false, - status: data.error, - }); - setToast(`ERROR: ${data.error}`,'danger'); - } - }); + } else if (data.status === 'FAILED') { + setIsLoading({ + flag: false, + status: data.error, + }); + setToast(`ERROR ${data.error}`, 'danger'); + } + }); + } }); }; @@ -356,44 +442,92 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } datasource: selectedItems[0]['label'], }; getJobId(skipQuery, http, (id) => { - pollQueryStatus(id, http, (data) => { - if (data.status === 'SUCCESS') { - if (data.resp.values.length > 0) { - setTreeData((prevTreeData) => { - return prevTreeData.map((database) => { - if (database.name === selectedDatabase) { - return { - ...database, - values: database.values?.map((table) => { - if (table.name === tableName) { - return { - ...table, - values: loadTreeItem( - [TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME], - TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME - ), - }; - } - return table; - }), - }; - } - return database; + if (id === undefined) { + const errorMessage = 'ERROR fetching Skipping index'; + setIsLoading({ + flag: false, + status: 'error', + }); + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === selectedDatabase) { + return { + ...database, + values: database.values?.map((table) => { + if (table.name === tableName) { + return { + ...table, + isLoading: false, + }; + } + return table; + }), + }; + } + return database; + }); + }); + setToast(errorMessage, 'danger'); + } else { + pollQueryStatus(id, http, (data) => { + if (data.status === 'SUCCESS') { + if (data.results.length > 0) { + setTreeData((prevTreeData) => { + return prevTreeData.map((database) => { + if (database.name === selectedDatabase) { + return { + ...database, + values: database.values?.map((table) => { + if (table.name === tableName) { + return { + ...table, + values: loadTreeItem( + [TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME], + TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME + ), + }; + } + return table; + }), + }; + } + return database; + }); }); + } + loadCoveringIndex(tableName); + } else if (data.status === 'FAILED') { + setIsLoading({ + flag: false, + status: data.error, }); + setToast(`ERROR ${data.error}`, 'danger'); } - loadCoveringIndex(tableName); - } else if (data.status === 'FAILED') { - setIsLoading({ - flag: false, - status: data.error, - }); - setToast(`ERROR: ${data.error}`,'danger'); - } - }); + }); + } }); }; + const iconCreation = (node: TreeItem) => { + if (node.type === TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME) { + return MV; + } else if ( + node.type === TREE_ITEM_BADGE_NAME || + node.type === TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME + ) { + return null; + } else if (node.type === TREE_ITEM_TABLE_NAME_DEFAULT_NAME) { + return ; + } else if (node.type === TREE_ITEM_DATABASE_NAME_DEFAULT_NAME) { + return ; + } else if ( + node.type === TREE_ITEM_COVERING_INDEX_DEFAULT_NAME || + TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME + ) { + return ; + } + }; + const createLabel = (node: TreeItem, parentName: string, index: number) => { switch (node.type) { case TREE_ITEM_BADGE_NAME: @@ -408,10 +542,16 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } case TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME: return (
- - Load Materialized View - - {node.isLoading && } + + + + Load Materialized View + + + + {node.isLoading && } + +
); @@ -448,7 +588,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } const treeDataDatabases = treeData.map((database, index) => ({ label: createLabel(database, selectedItems[0].label, index), - icon: , + icon: iconCreation(database), id: 'element_' + index, callback: () => { if (database.values?.length === 0 && selectedItems[0].label !== 'OpenSearch') { @@ -460,16 +600,12 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } children: database.values?.map((table, index) => ({ label: createLabel(table, database.name, index), id: `${database.name}_${table.name}`, - icon: - table.type === TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME ? null : table.type === - TREE_ITEM_BADGE_NAME ? null : ( - - ), + icon: iconCreation(table), callback: () => { - if (table.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME && table.values?.length === 0) { + if (table.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME && table.type !== TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME && table.values?.length === 0) { handleTableClick(table.name); } - if (table.type === TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME) { + if (table.type === TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME) { handleAccelerationIndexClick( 'materialized', selectedItems[0].label, @@ -483,7 +619,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree } children: table.values?.map((indexChild, index) => ({ label: createLabel(indexChild, table.name, index), id: `${database.name}_${table.name}_${indexChild.name}`, - icon: indexChild.type === TREE_ITEM_BADGE_NAME ? null : , + icon: iconCreation(indexChild), callback: () => { if (indexChild.type !== TREE_ITEM_BADGE_NAME) { handleAccelerationIndexClick(