Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Backport workbench fixes to 7.9 on old platform #940

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/sql-workbench-release-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ jobs:

- name: Move Workbench to Plugins Dir
run: |
mkdir kibana/plugins
mv workbench kibana/plugins

- name: Kibana Plugin Bootstrap
Expand Down
2 changes: 1 addition & 1 deletion plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ afterEvaluate {

license 'ASL-2.0'
maintainer 'OpenDistro for Elasticsearch Team <[email protected]>'
url 'https://opendistro.github.io/elasticsearch/downloads'
url 'https://opendistro.github.io/for-elasticsearch/downloads.html'
summary '''
SQL plugin for OpenDistro for Elasticsearch.
Reference documentation can be found at https://opendistro.github.io/for-elasticsearch-docs/.
Expand Down
1 change: 1 addition & 0 deletions workbench/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules/
/build/
.cypress/screenshots
.cypress/videos
/target/*
106 changes: 61 additions & 45 deletions workbench/public/components/Main/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { MESSAGE_TAB_LABEL } from "../../utils/constants";
interface ResponseData {
ok: boolean;
resp: any;
body: any;
}

export interface ResponseDetail<T> {
Expand All @@ -45,7 +46,7 @@ export interface QueryMessage {

export type QueryResult = {
fields: string[];
records: { [key: string]: any }[];
records: DataRow[];
message: string;
};

Expand All @@ -63,6 +64,11 @@ export type ItemIdToExpandedRowMap = {
}
};

export type DataRow = {
rowId: number
data: { [key: string]: any }
}

interface MainProps {
httpClient: IHttpService;
onChange: (id: string, value?: any) => void;
Expand All @@ -89,20 +95,24 @@ interface MainState {

const SUCCESS_MESSAGE = "Success";

// It gets column names and row values to display in a Table from the json API response
const errorQueryResponse = (queryResultResponseDetail: any) => {
let errorMessage = queryResultResponseDetail.errorMessage + ', this query is not runnable. \n \n' +
queryResultResponseDetail.data;
return errorMessage;
}

export function getQueryResultsForTable(queryResults: ResponseDetail<string>[]): ResponseDetail<QueryResult>[] {
return queryResults.map(
(queryResultResponseDetail: ResponseDetail<string>): ResponseDetail<QueryResult> => {
if (!queryResultResponseDetail.fulfilled) {
return {
fulfilled: queryResultResponseDetail.fulfilled,
errorMessage: queryResultResponseDetail.errorMessage
errorMessage: errorQueryResponse(queryResultResponseDetail),
};
} else {
let databaseRecords: { [key: string]: any }[] = [];
const responseObj = queryResultResponseDetail.data ? JSON.parse(queryResultResponseDetail.data) : '';
let databaseFields: string[] = [];
let fields: string[] = [];
let dataRows: DataRow[] = [];

const schema: object[] = _.get(responseObj, 'schema');
const datarows: any[][] = _.get(responseObj, 'datarows');
Expand All @@ -120,20 +130,24 @@ export function getQueryResultsForTable(queryResults: ResponseDetail<string>[]):

switch (queryType) {
case 'show':
databaseFields[0] = 'TABLE_NAME';
databaseFields.unshift('id');
fields[0] = 'TABLE_NAME';

let index: number = -1;
for (const [id, field] of schema.entries()) {
if (_.eq(_.get(field, 'name'), 'TABLE_NAME')) {
index = id;
break;
}
}
for (const [id, datarow] of datarows.entries()) {
let databaseRecord: { [key: string]: any } = {};
databaseRecord['id'] = id;
databaseRecord['TABLE_NAME'] = datarow[index];
databaseRecords.push(databaseRecord);

for (const [id, field] of datarows.entries()) {
let row: { [key: string]: any } = {};
row['TABLE_NAME'] = field[index];
let dataRow: DataRow = {
rowId: id,
data: row
};
dataRows[id] = dataRow;
}
break;

Expand All @@ -149,32 +163,33 @@ export function getQueryResultsForTable(queryResults: ResponseDetail<string>[]):
fields[id] = !alias ? _.get(field, 'name') : alias;
}
}
databaseFields = fields;
databaseFields.unshift("id");
for (const [id, datarow] of datarows.entries()) {
let databaseRecord: { [key: string]: any } = {};
databaseRecord['id'] = id;

for (const [id, data] of datarows.entries()) {
let row: { [key: string]: any } = {};
for (const index of schema.keys()) {
const fieldname = databaseFields[index + 1];
databaseRecord[fieldname] = datarow[index];
const fieldname = fields[index];
row[fieldname] = data[index];
}
databaseRecords.push(databaseRecord);
let dataRow: DataRow = {
rowId: id,
data: row
};
dataRows[id] = dataRow;
}
break;

default:
let databaseRecord: { [key: string]: any } = {};
databaseRecords.push(databaseRecord);
}

}
return {
fulfilled: queryResultResponseDetail.fulfilled,
data: {
fields: databaseFields,
records: databaseRecords,
message: SUCCESS_MESSAGE
}
}
fields: fields,
records: dataRows,
message: SUCCESS_MESSAGE,
},
};

}
}
);
Expand Down Expand Up @@ -248,7 +263,7 @@ export class Main extends React.Component<MainProps, MainState> {
return {
fulfilled: false,
errorMessage: response.data.resp,
data: ''
data: response.data.body,
};
}

Expand Down Expand Up @@ -325,22 +340,23 @@ export class Main extends React.Component<MainProps, MainState> {
const results: ResponseDetail<string>[] = response.map(response =>
this.processQueryResponse(response as IHttpResponse<ResponseData>));
const resultTable: ResponseDetail<QueryResult>[] = getQueryResultsForTable(results);

this.setState({
queries: queries,
queryResults: results,
queryResultsTable: resultTable,
selectedTabId: getDefaultTabId(results),
selectedTabName: getDefaultTabLabel(results, queries[0]),
messages: this.getMessage(resultTable),
itemIdToExpandedRowMap: {},
queryResultsJSON: [],
queryResultsCSV: [],
queryResultsTEXT: [],
searchQuery: ""
}, () => console.log("Successfully updated the states")); // added callback function to handle async issues
})

this.setState(
{
queries: queries,
queryResults: results,
queryResultsTable: resultTable,
selectedTabId: getDefaultTabId(results),
selectedTabName: getDefaultTabLabel(results, queries[0]),
messages: this.getMessage(resultTable),
itemIdToExpandedRowMap: {},
queryResultsJSON: [],
queryResultsCSV: [],
queryResultsTEXT: [],
searchQuery: '',
},
() => console.log('Successfully updated the states')
); // added callback function to handle async issues
});
}
};

Expand Down
13 changes: 12 additions & 1 deletion workbench/public/components/PPLPage/PPLPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ export class PPLPage extends React.Component<PPLPageProps, PPLPageState> {
const closeModal = () => this.setIsModalVisible(false);
const showModal = () => this.setIsModalVisible(true);

const pplTranslationsNotEmpty = () => {
if (this.props.pplTranslations.length > 0) {
return this.props.pplTranslations[0].fulfilled;
}
return false;
}

const explainContent = pplTranslationsNotEmpty()
? this.props.pplTranslations.map((queryTranslation: any) => JSON.stringify(queryTranslation.data, null, 2)).join("\n")
: 'This query is not explainable.';

let modal;

if (this.state.isModalVisible) {
Expand All @@ -85,7 +96,7 @@ export class PPLPage extends React.Component<PPLPageProps, PPLPageState> {
fontSize="m"
isCopyable
>
{this.props.pplTranslations.map((queryTranslation: any) => JSON.stringify(queryTranslation.data, null, 2)).join("\n")}
{explainContent}
</EuiCodeBlock>
</EuiModalBody>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ describe("<QueryResults with data/> spec", () => {
// It tests Tab button
await fireEvent.click(getAllByRole('tab')[5]);

// TODO: uncomment this test when sorting is fixed
// It tests sorting
await fireEvent.click(getAllByTestId('tableHeaderSortButton')[1]);
// await fireEvent.click(getAllByTestId('tableHeaderSortButton')[1]);

// It tests pagination
await fireEvent.click(getAllByLabelText('Page 2 of 2')[0]);
Expand Down Expand Up @@ -209,8 +210,9 @@ describe("<QueryResults with data/> spec", () => {
// It tests Tab button
await fireEvent.click(getAllByRole('tab')[5]);

// TODO: uncomment this test when sorting is fixed
// It tests sorting
await fireEvent.click(getAllByTestId('tableHeaderSortButton')[1]);
// await fireEvent.click(getAllByTestId('tableHeaderSortButton')[1]);

// It tests pagination
await fireEvent.click(getAllByLabelText('Page 2 of 2')[0]);
Expand Down
92 changes: 67 additions & 25 deletions workbench/public/components/QueryResults/QueryResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import React from "react";
// @ts-ignore
import { SortableProperties, SortableProperty } from "@elastic/eui/lib/services";
// @ts-ignore
import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, EuiPopover, EuiContextMenuItem, EuiContextMenuPanel, EuiHorizontalRule, EuiSearchBar, Pager, EuiIcon, EuiText, EuiSpacer, EuiTextAlign, EuiButton, EuiButtonIcon } from "@elastic/eui";
import { QueryResult, QueryMessage, Tab, ResponseDetail, ItemIdToExpandedRowMap } from "../Main/main";
import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, EuiPopover, EuiContextMenuItem, EuiContextMenuPanel, EuiHorizontalRule, EuiSearchBar, Pager, EuiIcon, EuiText, EuiSpacer, EuiTextAlign, EuiButton, EuiButtonIcon, Comparators } from "@elastic/eui";
import { QueryResult, QueryMessage, Tab, ResponseDetail, ItemIdToExpandedRowMap, DataRow } from "../Main/main";
import QueryResultsBody from "./QueryResultsBody";
import { getQueryIndex, needsScrolling, getSelectedResults } from "../../utils/utils";
import { DEFAULT_NUM_RECORDS_PER_PAGE, MESSAGE_TAB_LABEL, TAB_CONTAINER_ID } from "../../utils/constants";
Expand Down Expand Up @@ -137,29 +137,74 @@ class QueryResults extends React.Component<QueryResultsProps, QueryResultsState>
}

// Update SORTABLE COLUMNS - All columns
updateSortableColumns(queryResultsSelected: QueryResult): void {
if (this.sortableColumns.length === 0) {
queryResultsSelected.fields.map((field: string) => {
this.sortableColumns.push({
name: field,
getValue: (item: any) => item[field],
isAscending: true
});
updateSortableColumns = (queryResultsSelected: QueryResult) => {
if (this.sortableColumns.length != 0) {
this.sortableColumns = [];
}
queryResultsSelected.fields.map((field: string) => {
this.sortableColumns.push({
name: field,
getValue: (item: DataRow) => item.data[field],
isAscending: true
});
this.sortedColumn =
this.sortableColumns.length > 0 ? this.sortableColumns[0].name : "";
this.sortableProperties = new SortableProperties(
this.sortableColumns,
this.sortedColumn
);
});
this.sortedColumn =
this.sortableColumns.length > 0 ? this.sortableColumns[0].name : "";
this.sortableProperties = new SortableProperties(
this.sortableColumns,
this.sortedColumn
);
}

searchItems(dataRows: DataRow[], searchQuery: string): DataRow[] {
let rows: { [key: string]: any }[] = [];
for (const row of dataRows) {
rows.push(row.data)
}
const searchResult = EuiSearchBar.Query.execute(searchQuery, rows);
let result: DataRow[] = [];
for (const row of searchResult) {
let dataRow: DataRow = {
// rowId does not matter here since the data rows would be sorted later
rowId: 0,
data: row
}
result.push(dataRow)
}
return result;
}

onSort = (prop: string) => {
this.sortableProperties.sortOn(prop);
onSort = (prop: string, items: DataRow[]): DataRow[] => {
let sortedRows = this.sortDataRows(items, prop);
this.sortableProperties.sortOn(prop)
this.sortedColumn = prop;
this.setState({});
};
return sortedRows;
}

sortDataRows(dataRows: DataRow[], field: string): DataRow[] {
const property = this.sortableProperties.getSortablePropertyByName(field);
const copy = [...dataRows];
let comparator = (a: DataRow, b: DataRow) => {
if (typeof property === "undefined") {
return 0;
}
let dataA = a.data;
let dataB = b.data;
if (dataA[field] && dataB[field]) {
if (dataA[field] > dataB[field]) {
return 1;
}
if (dataA[field] < dataB[field]) {
return -1;
}
}
return 0;
}
if (!this.sortableProperties.isAscendingByName(field)) {
Comparators.reverse(comparator);
}
return copy.sort(comparator);
}

renderTabs(): Tab[] {
const tabs = [
Expand Down Expand Up @@ -193,10 +238,7 @@ class QueryResults extends React.Component<QueryResultsProps, QueryResultsState>

if (queryResultSelected) {
const matchingItems: object[] = this.props.searchQuery
? EuiSearchBar.Query.execute(
this.props.searchQuery,
queryResultSelected.records
)
? this.searchItems(queryResultSelected.records, this.props.searchQuery)
: queryResultSelected.records;
this.updatePagination(matchingItems.length);
this.updateSortableColumns(queryResultSelected);
Expand Down Expand Up @@ -354,7 +396,6 @@ class QueryResults extends React.Component<QueryResultsProps, QueryResultsState>
lastItemIndex={this.pager.getLastItemIndex()}
onChangeItemsPerPage={this.onChangeItemsPerPage}
onChangePage={this.onChangePage}
onSort={this.onSort}
sortedColumn={this.sortedColumn}
sortableProperties={this.sortableProperties}
itemIdToExpandedRowMap={this.props.itemIdToExpandedRowMap}
Expand All @@ -363,6 +404,7 @@ class QueryResults extends React.Component<QueryResultsProps, QueryResultsState>
getJdbc={this.props.getJdbc}
getCsv={this.props.getCsv}
getText={this.props.getText}
onSort={this.onSort}
/>
</PanelWrapper>
</>
Expand Down
Loading