-
spec renders the component 1`] = `
- .kibana_1
+
+ .kibana_1
+
spec", () => {
-
- it("renders the component", async () => {
-
+describe('
spec', () => {
+ it('renders the component', async () => {
const client = httpClientMock;
- client.post = jest.fn().mockResolvedValue(mockHttpQuery)
-
+ client.post = jest.fn().mockResolvedValue(mockHttpQuery);
+ client.get = jest.fn().mockResolvedValue(mockDatasourcesQuery);
const asyncTest = () => {
- render(
-
- );
+ render(
);
};
await asyncTest();
expect(document.body.children[0]).toMatchSnapshot();
});
-
- it("click run button, and response is ok", async () => {
+ it('click run button, and response is ok', async () => {
const client = httpClientMock;
client.post = jest.fn().mockResolvedValue(mockQueryResultJDBCResponse);
@@ -51,9 +46,10 @@ describe("
spec", () => {
expect(document.body.children[0]).toMatchSnapshot();
});
- it("click run button, response fills null and missing values", async () => {
+ it('click run button, response fills null and missing values', async () => {
const client = httpClientMock;
client.post = jest.fn().mockResolvedValue(mockResultWithNull);
+ client.get = jest.fn().mockResolvedValue(mockDatasourcesQuery);
const { getByText } = await render(
@@ -64,11 +60,12 @@ describe("
spec", () => {
};
await asyncTest();
expect(document.body.children[0]).toMatchSnapshot();
- })
+ });
- it("click run button, and response causes an error", async () => {
+ it('click run button, and response causes an error', async () => {
const client = httpClientMock;
client.post = jest.fn().mockRejectedValue('err');
+ client.get = jest.fn().mockResolvedValue(mockDatasourcesQuery);
const { getByText } = await render(
@@ -81,9 +78,10 @@ describe("
spec", () => {
expect(document.body.children[0]).toMatchSnapshot();
});
- it("click run button, and response is not ok", async () => {
+ it('click run button, and response is not ok', async () => {
const client = httpClientMock;
client.post = jest.fn().mockResolvedValue(mockNotOkQueryResultResponse);
+ client.get = jest.fn().mockResolvedValue(mockDatasourcesQuery);
const { getByText } = await render(
@@ -96,9 +94,11 @@ describe("
spec", () => {
expect(document.body.children[0]).toMatchSnapshot();
});
- it("click translation button, and response is ok", async () => {
+ it('click translation button, and response is ok', async () => {
const client = httpClientMock;
client.post = jest.fn().mockResolvedValue(mockQueryTranslationResponse);
+ client.get = jest.fn().mockResolvedValue(mockDatasourcesQuery);
+
const { getByText } = await render(
);
@@ -110,8 +110,10 @@ describe("
spec", () => {
expect(document.body.children[0]).toMatchSnapshot();
});
- it("click clear button", async () => {
+ it('click clear button', async () => {
const client = httpClientMock;
+ client.get = jest.fn().mockResolvedValue(mockDatasourcesQuery);
+
const { getByText } = await render(
);
diff --git a/public/components/Main/main.tsx b/public/components/Main/main.tsx
index 5c7defb0..f79c42db 100644
--- a/public/components/Main/main.tsx
+++ b/public/components/Main/main.tsx
@@ -5,7 +5,7 @@
import {
EuiButton,
- EuiComboBox,
+ EuiComboBoxOptionOption,
EuiFlexGroup,
EuiFlexItem,
EuiPage,
@@ -14,12 +14,13 @@ import {
EuiPageSideBar,
EuiPanel,
EuiSpacer,
- EuiText,
+ EuiText
} from '@elastic/eui';
import { IHttpResponse } from 'angular';
import _ from 'lodash';
import React from 'react';
import { ChromeBreadcrumb, CoreStart } from '../../../../../src/core/public';
+import { AsyncQueryLoadingStatus } from '../../../common/types';
import { MESSAGE_TAB_LABEL } from '../../utils/constants';
import {
Tree,
@@ -31,9 +32,9 @@ import {
import { PPLPage } from '../PPLPage/PPLPage';
import Switch from '../QueryLanguageSwitch/Switch';
import QueryResults from '../QueryResults/QueryResults';
+import { DataSelect } from '../SQLPage/DataSelect';
import { SQLPage } from '../SQLPage/SQLPage';
import { TableView } from '../SQLPage/TableView';
-import { AsyncQueryLoadingStatus } from '../../../common/types';
interface ResponseData {
ok: boolean;
@@ -101,7 +102,7 @@ interface MainState {
itemIdToExpandedRowMap: ItemIdToExpandedRowMap;
messages: Array
;
isResultFullScreen: boolean;
- selectedDatasource: string;
+ selectedDatasource: EuiComboBoxOptionOption[];
asyncLoading: boolean;
asyncLoadingStatus: AsyncQueryLoadingStatus;
asyncJobId: string;
@@ -238,7 +239,7 @@ export class Main extends React.Component {
itemIdToExpandedRowMap: {},
messages: [],
isResultFullScreen: false,
- selectedDatasource: 'Opensearch',
+ selectedDatasource: [{ label: 'OpenSearch' }],
asyncLoading: false,
asyncLoadingStatus: 'SUCCESS',
asyncJobId: '',
@@ -416,7 +417,7 @@ export class Main extends React.Component {
queries.map((query: string) =>
this.httpClient
.post(endpoint, {
- body: JSON.stringify({ lang: language, query: query, datasource: 'my_glue' }), // TODO: dynamically datasource when accurate
+ body: JSON.stringify({ lang: language, query: query, datasource: this.state.selectedDatasource[0].label}), // TODO: dynamically datasource when accurate
})
.catch((error: any) => {
this.setState({
@@ -781,9 +782,9 @@ export class Main extends React.Component {
});
}
- handleComboOptionChange = (selectedOption: string) => {
+ handleDataSelect = (selectedItems: []) => {
this.setState({
- selectedDatasource: selectedOption,
+ selectedDatasource: selectedItems,
});
};
@@ -796,7 +797,7 @@ export class Main extends React.Component {
page = (
{
page = (
{
Data Sources
- {
- const selectedValue = selectedOptions[0] ? selectedOptions[0].value : '';
- this.handleComboOptionChange(selectedValue);
- }}
- />
+
@@ -915,7 +902,7 @@ export class Main extends React.Component {
diff --git a/public/components/SQLPage/DataSelect.tsx b/public/components/SQLPage/DataSelect.tsx
new file mode 100644
index 00000000..bc63607a
--- /dev/null
+++ b/public/components/SQLPage/DataSelect.tsx
@@ -0,0 +1,76 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
+import React, { useEffect, useState } from 'react';
+import { CoreStart } from '../../../../../src/core/public';
+
+interface CustomView {
+ http: CoreStart['http'];
+ onSelect: (selectedItems: []) => void;
+}
+
+export const DataSelect = ({ http, onSelect }: CustomView) => {
+ const [selectedOptions, setSelectedOptions] = useState([{ label: 'OpenSearch' }]);
+ const [options, setOptions] = useState([]);
+
+ const datasources = () => {
+ http
+ .get(`/api/get_datasources`)
+ .then((res) => {
+ const data = res.data.resp;
+
+ const connectorGroups = {};
+
+ data.forEach((item) => {
+ const connector = item.connector;
+ const name = item.name;
+
+ if (connector === 'S3GLUE') {
+ if (!connectorGroups[connector]) {
+ connectorGroups[connector] = [];
+ }
+
+ connectorGroups[connector].push(name);
+ }
+ });
+ options.push({ label: 'OpenSearch' });
+ for (const connector in connectorGroups) {
+ if (connectorGroups.hasOwnProperty(connector)) {
+ const connectorNames = connectorGroups[connector];
+
+ options.push({
+ label: connector,
+ options: connectorNames.map((name) => ({ label: name })),
+ });
+ }
+ }
+
+ setOptions(options);
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+ };
+
+ useEffect(() => {
+ datasources();
+ }, []);
+
+ const handleSelectionChange = (selectedItems: any[]) => {
+ setSelectedOptions(selectedItems);
+ onSelect(selectedItems);
+ };
+
+ return (
+ handleSelectionChange(selectedItems)}
+ />
+ );
+};
diff --git a/public/components/SQLPage/TableView.tsx b/public/components/SQLPage/TableView.tsx
index 328a4330..3f168ccd 100644
--- a/public/components/SQLPage/TableView.tsx
+++ b/public/components/SQLPage/TableView.tsx
@@ -3,106 +3,161 @@
* SPDX-License-Identifier: Apache-2.0
*/
-
-import React, { useState, useEffect } from "react";
-import { EuiEmptyPrompt, EuiIcon, EuiTreeView } from "@elastic/eui";
+import { EuiEmptyPrompt, EuiIcon, EuiTreeView } from '@elastic/eui';
import _ from 'lodash';
+import React, { useEffect, useState } from 'react';
import { CoreStart } from '../../../../../src/core/public';
-import { ON_LOAD_QUERY } from "../../../common/constants";
+import { ON_LOAD_QUERY } from '../../../common/constants';
+import { getJobId, pollQueryStatus } from './utils';
interface CustomView {
- http: CoreStart['http'],
- dataConnection: string
+ http: CoreStart['http'];
+ selectedItems: any[];
}
-export const TableView = ({ http, dataConnection }: CustomView) => {
- const [tablenames, setTablenames] = useState([]);
- const [selectedNode, setSelectedNode] = useState(null);
- const [childData, setChildData] = useState([]);
- const [selectedChildNode, setSelectedChildNode] = useState(null);
- const [indexData, setIndexedData] = useState([]);
-
- const getSidebarContent = () => {
- const query = { query: ON_LOAD_QUERY }
- http
- .post(`/api/sql_console/sqlquery`, {
- body: JSON.stringify(query),
- })
- .then((res) => {
- const responseObj = res.data.resp
- ? JSON.parse(res.data.resp)
- : '';
- const datarows: any[][] = _.get(responseObj, 'datarows');
- const fields = datarows.map((data) => {
- return data[2]
- })
- setTablenames(fields)
- })
- .catch((err) => {
- console.error(err);
- });
+export const TableView = ({ http, selectedItems }: CustomView) => {
+ const [tablenames, setTablenames] = useState([]);
+ const [selectedNode, setSelectedNode] = useState(null);
+ const [childData, setChildData] = useState([]);
+ const [selectedChildNode, setSelectedChildNode] = useState(null);
+ const [indexData, setIndexedData] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ const [indiciesData, setIndiciesData] = useState([]);
+
+ const get_async_query_results = (id, http, callback) => {
+ pollQueryStatus(id, http, callback);
+ };
+
+ const getSidebarContent = () => {
+ if (selectedItems[0].label == 'OpenSearch') {
+ setTablenames([]);
+ const query = { query: ON_LOAD_QUERY };
+ http
+ .post(`/api/sql_console/sqlquery`, {
+ body: JSON.stringify(query),
+ })
+ .then((res) => {
+ const responseObj = res.data.resp ? JSON.parse(res.data.resp) : '';
+ const datarows: any[][] = _.get(responseObj, 'datarows');
+ const fields = datarows.map((data) => {
+ return data[2];
+ });
+ setTablenames(fields);
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+ } else {
+ setTablenames([]);
+ const query = {
+ lang: 'sql',
+ query: `SHOW SCHEMAS IN ${selectedItems[0]['label']}`,
+ datasource: selectedItems[0]['label'],
+ };
+ getJobId(query, http, (id) => {
+ get_async_query_results(id, http, (data) => {
+ setTablenames(data);
+ });
+ });
+ }
+ };
+
+ useEffect(() => {
+ getSidebarContent();
+ }, [selectedItems[0]['label']]);
+
+ const handleNodeClick = (nodeLabel: string) => {
+ setSelectedNode(nodeLabel);
+ const query = {
+ lang: 'sql',
+ query: `SHOW TABLES IN ${selectedItems[0]['label']}.${nodeLabel}`,
+ datasource: selectedItems[0]['label'],
};
-
- useEffect(() => {
-
- getSidebarContent();
- }, []);
-
- const handleNodeClick = (nodeLabel: string) => {
-
- // // will update after new query
-
- const newData = ["Child 1", "Child 2", "Child 3"];
- setChildData(newData);
- setSelectedNode(nodeLabel);
+ getJobId(query, http, (id) => {
+ get_async_query_results(id, http, (data) => {
+ data = data.map((subArray) => subArray[1]);
+ setChildData(data);
+ });
+ });
+ };
+
+ const callCoverQuery = (nodeLabel1: string) => {
+ const coverQuery = {
+ lang: 'sql',
+ query: `SHOW INDEX ON ${selectedItems[0]['label']}.${selectedNode}.${nodeLabel1}`,
+ datasource: selectedItems[0]['label'],
};
-
- const handleChildClick = (nodeLabel1: string) => {
-
- // will update after new query
-
- const newData1 = ["Child 4", "Child 5", "Child 6"];
- setIndexedData(newData1);
- setSelectedChildNode(nodeLabel1);
+ getJobId(coverQuery, http, (id) => {
+ get_async_query_results(id, http, (data) => {
+ data = [].concat(...data)
+ indiciesData.push(data)
+ setIndexedData(indiciesData);
+ });
+ });
+ };
+ const handleChildClick = (nodeLabel1: string) => {
+ setSelectedChildNode(nodeLabel1);
+ const skipQuery = {
+ lang: 'sql',
+ query: `DESC SKIPPING INDEX ON ${selectedItems[0]['label']}.${selectedNode}.${nodeLabel1}`,
+ datasource: selectedItems[0]['label'],
};
-
-
- const treeData = tablenames.map((element, index) => ({
- label: element,
- icon: ,
- id: 'element_' + index,
- callback: () => handleNodeClick(element),
- isSelectable: true,
- isExpanded: true,
- children: dataConnection === 'S3' && selectedNode === element ? childData.map(child => ({
- label: child,
- id: `${element}_${child}`,
- icon: ,
- callback: () => handleChildClick(child),
- sSelectable: true,
+ getJobId(skipQuery, http, (id) => {
+ get_async_query_results(id, http, (data) => {
+ if (data.length > 0) {
+ indiciesData.push('skip_index');
+ callCoverQuery(nodeLabel1);
+ }
+ });
+ });
+ };
+
+ const treeData = tablenames.map((database, index) => ({
+ label: {database}
,
+ icon: ,
+ id: 'element_' + index,
+ callback: () => {
+ setChildData([]);
+ setIsLoading(true);
+ handleNodeClick(database);
+ },
+ isSelectable: true,
+ isExpanded: true,
+ children:
+ selectedNode === database
+ ? childData.map((table) => ({
+ label: {table}
,
+ id: `${database}_${table}`,
+ icon: ,
+ callback: () => {
+ setIndexedData([]);
+ handleChildClick(table);
+ },
+ sSelectable: true,
isExpanded: true,
- children: selectedChildNode === child ? indexData.map(indexChild => ({
- label: indexChild,
- id: `${child}_${indexChild}`,
- icon:
- })):undefined,
- })) : undefined,
- }));
-
- return (
- <>
- {treeData.length > 0 ?
-
- : Error loading Datasources}
- />
- }
- >
- )
-}
-
+ children:
+ selectedChildNode === table
+ ? indexData.map((indexChild) => ({
+ label: indexChild,
+ id: `${table}_${indexChild}`,
+ icon: ,
+ }))
+ : undefined,
+ }))
+ : undefined,
+ }));
+
+ return (
+ <>
+ {treeData.length > 0 ? (
+
+ ) : (
+ Error loading Datasources}
+ />
+ )}
+ >
+ );
+};
diff --git a/public/components/SQLPage/utils.tsx b/public/components/SQLPage/utils.tsx
new file mode 100644
index 00000000..48909ab8
--- /dev/null
+++ b/public/components/SQLPage/utils.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+const POLL_INTERVAL_MS = 5000;
+import _ from 'lodash';
+import { CoreStart } from '../../../../../src/core/public';
+
+
+
+export const pollQueryStatus = (id: string, http :CoreStart['http'], callback) => {
+ http
+ .get(`/api/spark_sql_console/job/` + id)
+ .then((res) => {
+ const status = res.data.resp.status;
+ if (status === 'PENDING' || status === 'RUNNING' || status === 'SCHEDULED') {
+ setTimeout(() => pollQueryStatus(id, http, callback), POLL_INTERVAL_MS);
+ } else if (status === 'FAILED') {
+ callback([]);
+ } else if (status === 'SUCCESS') {
+ const results = _.get(res.data.resp, 'datarows');
+ callback(results);
+ }
+ })
+ .catch((err) => {
+ console.error(err);
+ callback([]);
+ });
+};
+
+export const getJobId = (query: {}, http: CoreStart['http'], callback) => {
+ let id;
+ http
+ .post(`/api/spark_sql_console`, {
+ body: JSON.stringify(query),
+ })
+ .then((res) => {
+ id = res.data.resp.queryId;
+ callback(id)
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+};
diff --git a/server/clusters/sql/sqlPlugin.js b/server/clusters/sql/sqlPlugin.js
index 3f0b210d..e23a068f 100644
--- a/server/clusters/sql/sqlPlugin.js
+++ b/server/clusters/sql/sqlPlugin.js
@@ -4,7 +4,7 @@
*/
-import { SQL_TRANSLATE_ROUTE, SQL_QUERY_ROUTE, PPL_QUERY_ROUTE, PPL_TRANSLATE_ROUTE, FORMAT_CSV, FROMAT_JDBC, FORMAT_JSON, FORMAT_TEXT, SPARK_SQL_QUERY_ROUTE } from '../../services/utils/constants';
+import { SQL_TRANSLATE_ROUTE, SQL_QUERY_ROUTE, PPL_QUERY_ROUTE, PPL_TRANSLATE_ROUTE, FORMAT_CSV, FROMAT_JDBC, FORMAT_JSON, FORMAT_TEXT, SPARK_SQL_QUERY_ROUTE, DATASOURCES_GET_QUERY } from '../../services/utils/constants';
export default function sqlPlugin(Client, config, components) {
const ca = components.clientAction.factory;
@@ -114,6 +114,14 @@ export default function sqlPlugin(Client, config, components) {
method: 'GET',
});
+ sql.datasourcesGetQuery = ca({
+ url: {
+ fmt: `${DATASOURCES_GET_QUERY}`,
+ },
+ needBody: false,
+ method: 'GET',
+ });
+
sql.asyncDeleteQuery = ca({
url: {
fmt: `${SPARK_SQL_QUERY_ROUTE}/<%=jobId%>`,
diff --git a/server/routes/query.ts b/server/routes/query.ts
index a4b0b376..95e6b6ba 100644
--- a/server/routes/query.ts
+++ b/server/routes/query.ts
@@ -8,16 +8,17 @@ import { schema } from '@osd/config-schema';
import { IOpenSearchDashboardsResponse, IRouter, ResponseError } from '../../../../src/core/server';
import QueryService from '../services/QueryService';
import {
- ROUTE_PATH_SQL_QUERY,
- ROUTE_PATH_PPL_QUERY,
- ROUTE_PATH_SQL_CSV,
- ROUTE_PATH_SQL_JSON,
- ROUTE_PATH_SQL_TEXT,
+ ROUTE_PATH_GET_DATASOURCES,
ROUTE_PATH_PPL_CSV,
ROUTE_PATH_PPL_JSON,
+ ROUTE_PATH_PPL_QUERY,
ROUTE_PATH_PPL_TEXT,
- ROUTE_PATH_SPARK_SQL_QUERY,
ROUTE_PATH_SPARK_SQL_JOB_QUERY,
+ ROUTE_PATH_SPARK_SQL_QUERY,
+ ROUTE_PATH_SQL_CSV,
+ ROUTE_PATH_SQL_JSON,
+ ROUTE_PATH_SQL_QUERY,
+ ROUTE_PATH_SQL_TEXT
} from '../utils/constants';
export default function query(server: IRouter, service: QueryService) {
@@ -189,4 +190,21 @@ export default function query(server: IRouter, service: QueryService) {
});
}
)
+
+ server.get(
+ {
+ path: ROUTE_PATH_GET_DATASOURCES,
+ validate: {
+
+ },
+ },
+ async (context, request, response): Promise> => {
+ const retVal = await service.describeSyncQueryDataSources(request);
+ return response.ok({
+ body: retVal,
+ });
+ }
+ )
+
+
}
diff --git a/server/services/QueryService.ts b/server/services/QueryService.ts
index 9aa927ce..5d0bb5ad 100644
--- a/server/services/QueryService.ts
+++ b/server/services/QueryService.ts
@@ -5,8 +5,8 @@
import 'core-js/stable';
-import 'regenerator-runtime/runtime';
import _ from 'lodash';
+import 'regenerator-runtime/runtime';
export default class QueryService {
private client: any;
@@ -62,6 +62,28 @@ export default class QueryService {
}
};
+ describeQueryGetInternalSync = async (request: any, format: string, responseFormat: string) => {
+ try {
+ const queryResponse = await this.client.asScoped(request).callAsCurrentUser(format);
+ return {
+ data: {
+ ok: true,
+ resp: _.isEqual(responseFormat, 'json') ? JSON.stringify(queryResponse) : queryResponse,
+ },
+ };
+ } catch (err) {
+ console.log(err);
+ return {
+ data: {
+ ok: false,
+ resp: err.message,
+ body: err.body
+ },
+ };
+ }
+ };
+
+
describeSQLQuery = async (request: any) => {
return this.describeQueryPostInternal(request, 'sql.sqlQuery', 'json', request.body);
};
@@ -101,8 +123,10 @@ export default class QueryService {
describeSQLAsyncGetQuery = async (request: any, jobId: string) => {
return this.describeQueryJobIdInternal(request, 'sql.sparkSqlGetQuery', jobId, null);
};
-
+ describeSyncQueryDataSources = async (request: any) => {
+ return this.describeQueryGetInternalSync(request, 'sql.datasourcesGetQuery', null);
+ };
describeAsyncDeleteQuery = async (request: any, jobId: string) => {
return this.describeQueryJobIdInternal(request, 'sql.asyncDeleteQuery', jobId, null);
};
-}
+}
\ No newline at end of file
diff --git a/server/services/utils/constants.ts b/server/services/utils/constants.ts
index 2d103254..04c9d4d0 100644
--- a/server/services/utils/constants.ts
+++ b/server/services/utils/constants.ts
@@ -4,7 +4,6 @@
*/
-import { ParsedUrlQuery } from 'querystring';
export const SQL_TRANSLATE_ROUTE = `/_plugins/_sql/_explain`;
export const PPL_TRANSLATE_ROUTE = `/_plugins/_ppl/_explain`;
@@ -14,6 +13,7 @@ export const FORMAT_CSV = `format=csv`;
export const FORMAT_JSON = `format=json`;
export const FORMAT_TEXT = `format=raw`;
export const SPARK_SQL_QUERY_ROUTE = `/_plugins/_async_query`;
+export const DATASOURCES_GET_QUERY = `/_plugins/_query/_datasources`
export const DEFAULT_HEADERS = {
'Content-Type': 'application/json',
diff --git a/server/utils/constants.ts b/server/utils/constants.ts
index da6b6522..d6e4d4bf 100644
--- a/server/utils/constants.ts
+++ b/server/utils/constants.ts
@@ -14,4 +14,5 @@ export const ROUTE_PATH_PPL_JSON = '/api/sql_console/ppljson';
export const ROUTE_PATH_SQL_TEXT = '/api/sql_console/sqltext';
export const ROUTE_PATH_PPL_TEXT = '/api/sql_console/ppltext';
export const ROUTE_PATH_SPARK_SQL_QUERY = '/api/spark_sql_console';
-export const ROUTE_PATH_SPARK_SQL_JOB_QUERY = '/api/spark_sql_console/job';
\ No newline at end of file
+export const ROUTE_PATH_SPARK_SQL_JOB_QUERY = '/api/spark_sql_console/job';
+export const ROUTE_PATH_GET_DATASOURCES = '/api/get_datasources'
\ No newline at end of file
diff --git a/test/mocks/mockData.ts b/test/mocks/mockData.ts
index 2daadf9d..65904eec 100644
--- a/test/mocks/mockData.ts
+++ b/test/mocks/mockData.ts
@@ -2340,4 +2340,13 @@ export const mockHttpQuery =
"ok": true,
resp: "{\"schema\":[{\"name\":\"TABLE_CAT\",\"type\":\"keyword\"}],\"datarows\":[[\"opensearch\",null,\".kibana_1\",\"BASE TABLE\",null,null,null,null,null,null]]}"
}
-}
\ No newline at end of file
+}
+export const mockDatasourcesQuery =
+{
+ "data": {
+ "ok": true,
+ resp: "[{ \"name\": \"my_glue\", \"description\": \"\", \"connector\": \"S3GLUE\", \"allowedRoles\": [], \"properties\": { \"glue.indexstore.opensearch.uri\": \"\", \"glue.indexstore.opensearch.region\": \"\" }}]"
+ }
+}
+
+
diff --git a/yarn.lock b/yarn.lock
index e641c8ce..476c75cd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2639,4 +2639,4 @@ yauzl@^2.10.0:
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
- integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
\ No newline at end of file