diff --git a/common/constants/shared.ts b/common/constants/shared.ts index f26a59820..9bda5d3be 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -58,8 +58,8 @@ export const observabilityIntegrationsID = 'integrations'; export const observabilityIntegrationsTitle = 'Integrations'; export const observabilityIntegrationsPluginOrder = 9020; -export const observabilityDataConnectionsID = 'dataconnections'; -export const observabilityDataConnectionsTitle = 'Data Connections'; +export const observabilityDataConnectionsID = 'datasources'; +export const observabilityDataConnectionsTitle = 'Data sources'; export const observabilityDataConnectionsPluginOrder = 9030; // Shared Constants diff --git a/common/types/data_connections.ts b/common/types/data_connections.ts index 9fd0527d9..b19b1bb7d 100644 --- a/common/types/data_connections.ts +++ b/common/types/data_connections.ts @@ -3,8 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { EuiComboBoxOptionOption } from '@elastic/eui'; + export interface PermissionsConfigurationProps { - roles: Array<{ label: string }>; - selectedRoles: Array<{ label: string }>; - setSelectedRoles: React.Dispatch>>; + roles: Role[]; + selectedRoles: Role[]; + setSelectedRoles: React.Dispatch>; + layout: 'horizontal' | 'vertical'; } + +export type Role = EuiComboBoxOptionOption; + +export type DatasourceType = 'SPARK' | 'S3GLUE' | 'OPENSEARCH'; diff --git a/public/components/app.tsx b/public/components/app.tsx index 7ace523b4..805ebc2e0 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -19,7 +19,7 @@ import { EventAnalytics } from './event_analytics'; import { Home as MetricsHome } from './metrics/index'; import { Main as NotebooksHome } from './notebooks/components/main'; import { Home as TraceAnalyticsHome } from './trace_analytics/home'; -import { Home as DataConnectionsHome } from './data_connections/home'; +import { Home as DataConnectionsHome } from './datasources/home'; interface ObservabilityAppDeps { CoreStartProp: CoreStart; diff --git a/public/components/data_connections/components/data_connections_header.tsx b/public/components/data_connections/components/data_connections_header.tsx deleted file mode 100644 index 7f071683b..000000000 --- a/public/components/data_connections/components/data_connections_header.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { - EuiLink, - EuiPageHeader, - EuiPageHeaderSection, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import _ from 'lodash'; -import React from 'react'; -import { OPENSEARCH_DOCUMENTATION_URL } from '../../../../common/constants/data_connections'; - -export const DataConnectionsHeader = () => { - return ( -
- - - -

Data connections

-
-
-
- - - Connect and manage compatible OpenSearch and OpenSearch Dashboard data connections.{' '} - - Learn more - - - -
- ); -}; diff --git a/public/components/data_connections/components/__tests__/__snapshots__/data_connection.test.tsx.snap b/public/components/datasources/components/__tests__/__snapshots__/data_connection.test.tsx.snap similarity index 100% rename from public/components/data_connections/components/__tests__/__snapshots__/data_connection.test.tsx.snap rename to public/components/datasources/components/__tests__/__snapshots__/data_connection.test.tsx.snap diff --git a/public/components/data_connections/components/__tests__/__snapshots__/manage_data_connections_description.test.tsx.snap b/public/components/datasources/components/__tests__/__snapshots__/manage_data_connections_description.test.tsx.snap similarity index 91% rename from public/components/data_connections/components/__tests__/__snapshots__/manage_data_connections_description.test.tsx.snap rename to public/components/datasources/components/__tests__/__snapshots__/manage_data_connections_description.test.tsx.snap index 2cc84b45b..10c1b2484 100644 --- a/public/components/data_connections/components/__tests__/__snapshots__/manage_data_connections_description.test.tsx.snap +++ b/public/components/datasources/components/__tests__/__snapshots__/manage_data_connections_description.test.tsx.snap @@ -9,7 +9,7 @@ exports[`Manage Data Connections Description test Renders manage data connection

- Manage existing data connections + Manage existing data sources

- Manage already created data connections. + Manage already created data sources. diff --git a/public/components/data_connections/components/__tests__/__snapshots__/manage_data_connections_table.test.tsx.snap b/public/components/datasources/components/__tests__/__snapshots__/manage_data_connections_table.test.tsx.snap similarity index 96% rename from public/components/data_connections/components/__tests__/__snapshots__/manage_data_connections_table.test.tsx.snap rename to public/components/datasources/components/__tests__/__snapshots__/manage_data_connections_table.test.tsx.snap index 20c847340..6cd9a84f9 100644 --- a/public/components/data_connections/components/__tests__/__snapshots__/manage_data_connections_table.test.tsx.snap +++ b/public/components/datasources/components/__tests__/__snapshots__/manage_data_connections_table.test.tsx.snap @@ -19,7 +19,7 @@ exports[`Manage Data Connections Table test Renders manage data connections tabl class="euiTitle euiTitle--large" data-test-subj="dataconnections-header" > - Data connections + Data sources @@ -32,7 +32,7 @@ exports[`Manage Data Connections Table test Renders manage data connections tabl
- Connect and manage compatible OpenSearch and OpenSearch Dashboard data connections. + Connect and manage compatible OpenSearch and OpenSearch Dashboard data sources. +
+ + +
+
- Manage existing data connections + Manage existing data sources
- Manage already created data connections. + Manage already created data sources.

({ diff --git a/public/components/data_connections/components/__tests__/manage_data_connections_description.test.tsx b/public/components/datasources/components/__tests__/manage_data_connections_description.test.tsx similarity index 86% rename from public/components/data_connections/components/__tests__/manage_data_connections_description.test.tsx rename to public/components/datasources/components/__tests__/manage_data_connections_description.test.tsx index 686230683..3bbfa820e 100644 --- a/public/components/data_connections/components/__tests__/manage_data_connections_description.test.tsx +++ b/public/components/datasources/components/__tests__/manage_data_connections_description.test.tsx @@ -7,7 +7,7 @@ import { configure, mount } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import { waitFor } from '@testing-library/react'; import React from 'react'; -import { DataConnectionsDescription } from '../manage_data_connections_description'; +import { DataConnectionsDescription } from '../manage/manage_data_connections_description'; describe('Manage Data Connections Description test', () => { configure({ adapter: new Adapter() }); diff --git a/public/components/data_connections/components/__tests__/manage_data_connections_table.test.tsx b/public/components/datasources/components/__tests__/manage_data_connections_table.test.tsx similarity index 81% rename from public/components/data_connections/components/__tests__/manage_data_connections_table.test.tsx rename to public/components/datasources/components/__tests__/manage_data_connections_table.test.tsx index cd5509f1b..9c4979cff 100644 --- a/public/components/data_connections/components/__tests__/manage_data_connections_table.test.tsx +++ b/public/components/datasources/components/__tests__/manage_data_connections_table.test.tsx @@ -7,10 +7,17 @@ import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import { act } from '@testing-library/react'; import React from 'react'; -import { ManageDataConnectionsTable } from '../manage_data_connections_table'; +import { ManageDataConnectionsTable } from '../manage/manage_data_connections_table'; import { showDataConnectionsData } from '../../../../../test/datasources'; import ReactDOM from 'react-dom'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: () => ({ + pathname: '/manage', + }), +})); + describe('Manage Data Connections Table test', () => { configure({ adapter: new Adapter() }); diff --git a/public/components/datasources/components/data_connections_header.tsx b/public/components/datasources/components/data_connections_header.tsx new file mode 100644 index 000000000..6f5c87092 --- /dev/null +++ b/public/components/datasources/components/data_connections_header.tsx @@ -0,0 +1,77 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiLink, + EuiPageHeader, + EuiPageHeaderSection, + EuiSpacer, + EuiTab, + EuiTabs, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { OPENSEARCH_DOCUMENTATION_URL } from '../../../../common/constants/data_connections'; + +const tabs = [ + { + id: 'manage', + name: 'Manage data source', + disabled: false, + }, + { + id: 'new', + name: 'New data source', + disabled: false, + }, +]; + +export const DataConnectionsHeader = () => { + const location = useLocation().pathname.substring(1); + + const [selectedTabId, setSelectedTabId] = useState(location ? location : 'manage'); + + const onSelectedTabChanged = (id) => { + setSelectedTabId(id); + window.location.hash = id; + }; + + const renderTabs = () => { + return tabs.map((tab, index) => ( + onSelectedTabChanged(tab.id)} + isSelected={tab.id === selectedTabId} + disabled={tab.disabled} + key={index} + > + {tab.name} + + )); + }; + + return ( +
+ + + +

Data sources

+
+
+
+ + + Connect and manage compatible OpenSearch and OpenSearch Dashboard data sources.{' '} + + Learn more + + + + {renderTabs()} + +
+ ); +}; diff --git a/public/components/data_connections/components/access_control_tab.tsx b/public/components/datasources/components/manage/access_control_tab.tsx similarity index 85% rename from public/components/data_connections/components/access_control_tab.tsx rename to public/components/datasources/components/manage/access_control_tab.tsx index 58fdffde3..40e9e8ecd 100644 --- a/public/components/data_connections/components/access_control_tab.tsx +++ b/public/components/datasources/components/manage/access_control_tab.tsx @@ -14,23 +14,27 @@ import { import React, { useEffect, useState } from 'react'; import { EuiPanel } from '@elastic/eui'; import { ConnectionManagementCallout } from './connection_management_callout'; -import { coreRefs } from '../../../../public/framework/core_refs'; +import { coreRefs } from '../../../../framework/core_refs'; import { QueryPermissionsConfiguration } from './query_permissions'; -import { DATACONNECTIONS_BASE } from '../../../../common/constants/shared'; -import { SaveOrCancel } from './save_or_cancel'; +import { DATACONNECTIONS_BASE } from '../../../../../common/constants/shared'; +import { SaveOrCancel } from '../save_or_cancel'; +import { Role } from '../../../../../common/types/data_connections'; interface AccessControlTabProps { dataConnection: string; connector: string; properties: unknown; + allowedRoles: string[]; } export const AccessControlTab = (props: AccessControlTabProps) => { const [mode, setMode] = useState<'view' | 'edit'>('view'); - const [roles, setRoles] = useState>([]); - const [selectedQueryPermissionRoles, setSelectedQueryPermissionRoles] = useState< - Array<{ label: string }> - >([]); + const [roles, setRoles] = useState([]); + const [selectedQueryPermissionRoles, setSelectedQueryPermissionRoles] = useState( + props.allowedRoles.map((role) => { + return { label: role }; + }) + ); const { http } = coreRefs; useEffect(() => { @@ -51,7 +55,7 @@ export const AccessControlTab = (props: AccessControlTabProps) => { Query access - {[].length ? `Restricted` : '-'} + {selectedQueryPermissionRoles.length ? `Restricted` : '-'} @@ -67,6 +71,7 @@ export const AccessControlTab = (props: AccessControlTabProps) => { roles={roles} selectedRoles={selectedQueryPermissionRoles} setSelectedRoles={setSelectedQueryPermissionRoles} + layout={'vertical'} /> ); diff --git a/public/components/data_connections/components/connection_configuration.tsx b/public/components/datasources/components/manage/connection_configuration.tsx similarity index 100% rename from public/components/data_connections/components/connection_configuration.tsx rename to public/components/datasources/components/manage/connection_configuration.tsx diff --git a/public/components/data_connections/components/connection_details.tsx b/public/components/datasources/components/manage/connection_details.tsx similarity index 96% rename from public/components/data_connections/components/connection_details.tsx rename to public/components/datasources/components/manage/connection_details.tsx index 44cd206f4..533071960 100644 --- a/public/components/data_connections/components/connection_details.tsx +++ b/public/components/datasources/components/manage/connection_details.tsx @@ -14,9 +14,9 @@ import { import React, { useState } from 'react'; import { EuiPanel } from '@elastic/eui'; import { ConnectionManagementCallout } from './connection_management_callout'; -import { coreRefs } from '../../../../public/framework/core_refs'; -import { DATACONNECTIONS_BASE } from '../../../../common/constants/shared'; -import { SaveOrCancel } from './save_or_cancel'; +import { coreRefs } from '../../../../framework/core_refs'; +import { DATACONNECTIONS_BASE } from '../../../../../common/constants/shared'; +import { SaveOrCancel } from '../save_or_cancel'; import { ConnectionConfiguration } from './connection_configuration'; interface ConnectionDetailProps { diff --git a/public/components/data_connections/components/connection_management_callout.tsx b/public/components/datasources/components/manage/connection_management_callout.tsx similarity index 100% rename from public/components/data_connections/components/connection_management_callout.tsx rename to public/components/datasources/components/manage/connection_management_callout.tsx diff --git a/public/components/data_connections/components/data_connection.tsx b/public/components/datasources/components/manage/data_connection.tsx similarity index 93% rename from public/components/data_connections/components/data_connection.tsx rename to public/components/datasources/components/manage/data_connection.tsx index d4c87ef49..a0527e7bc 100644 --- a/public/components/data_connections/components/data_connection.tsx +++ b/public/components/datasources/components/manage/data_connection.tsx @@ -21,9 +21,9 @@ import { } from '@elastic/eui'; import React, { useEffect, useState } from 'react'; import { AccessControlTab } from './access_control_tab'; -import { NoAccess } from './no_access'; -import { DATACONNECTIONS_BASE } from '../../../../common/constants/shared'; -import { coreRefs } from '../../../../public/framework/core_refs'; +import { NoAccess } from '../no_access'; +import { DATACONNECTIONS_BASE } from '../../../../../common/constants/shared'; +import { coreRefs } from '../../../../framework/core_refs'; import { ConnectionDetails } from './connection_details'; interface DatasourceDetails { @@ -49,7 +49,7 @@ export const DataConnection = (props: any) => { useEffect(() => { chrome!.setBreadcrumbs([ { - text: 'Data Connections', + text: 'Data sources', href: '#/', }, { @@ -59,15 +59,15 @@ export const DataConnection = (props: any) => { ]); http! .get(`${DATACONNECTIONS_BASE}/${dataSource}`) - .then((data) => + .then((data) => { setDatasourceDetails({ allowedRoles: data.allowedRoles, name: data.name, cluster: data.properties['emr.cluster'], connector: data.connector, properties: data.properties, - }) - ) + }); + }) .catch((err) => { setHasAccess(false); }); @@ -80,9 +80,11 @@ export const DataConnection = (props: any) => { disabled: false, content: ( ), }, @@ -200,7 +202,7 @@ export const DataConnection = (props: any) => { - + diff --git a/public/components/data_connections/components/manage_data_connections_description.tsx b/public/components/datasources/components/manage/manage_data_connections_description.tsx similarity index 82% rename from public/components/data_connections/components/manage_data_connections_description.tsx rename to public/components/datasources/components/manage/manage_data_connections_description.tsx index 26cfa90cc..866ac5ae3 100644 --- a/public/components/data_connections/components/manage_data_connections_description.tsx +++ b/public/components/datasources/components/manage/manage_data_connections_description.tsx @@ -10,12 +10,12 @@ export const DataConnectionsDescription = () => { return (
-

Manage existing data connections

+

Manage existing data sources

- Manage already created data connections. + Manage already created data sources.
diff --git a/public/components/data_connections/components/manage_data_connections_table.tsx b/public/components/datasources/components/manage/manage_data_connections_table.tsx similarity index 89% rename from public/components/data_connections/components/manage_data_connections_table.tsx rename to public/components/datasources/components/manage/manage_data_connections_table.tsx index e1950e704..d2009fb1f 100644 --- a/public/components/data_connections/components/manage_data_connections_table.tsx +++ b/public/components/datasources/components/manage/manage_data_connections_table.tsx @@ -17,15 +17,17 @@ import { } from '@elastic/eui'; import _ from 'lodash'; import React, { useEffect, useState } from 'react'; -import { DataConnectionsHeader } from './data_connections_header'; -import { HomeProps } from '../home'; +import { DataConnectionsHeader } from '../data_connections_header'; +import { HomeProps } from '../../home'; import { DataConnectionsDescription } from './manage_data_connections_description'; -import { DATACONNECTIONS_BASE } from '../../../../common/constants/shared'; -import { useToast } from '../../../../public/components/common/toast'; -import { DeleteModal } from '../../../../public/components/common/helpers/delete_modal'; +import { DATACONNECTIONS_BASE } from '../../../../../common/constants/shared'; +import { useToast } from '../../../common/toast'; +import { DeleteModal } from '../../../common/helpers/delete_modal'; +import S3Logo from '../../icons/s3-logo.svg'; +import { DatasourceType } from '../../../../../common/types/data_connections'; interface DataConnection { - connectionType: 'OPENSEARCH' | 'SPARK'; + connectionType: DatasourceType; name: string; } @@ -57,7 +59,7 @@ export const ManageDataConnectionsTable = (props: HomeProps) => { useEffect(() => { chrome.setBreadcrumbs([ { - text: 'Data Connections', + text: 'Data sources', href: '#/', }, ]); @@ -93,8 +95,8 @@ export const ManageDataConnectionsTable = (props: HomeProps) => { const icon = (record: DataConnection) => { switch (record.connectionType) { - case 'OPENSEARCH': - return ; + case 'S3GLUE': + return ; default: return <>; } diff --git a/public/components/data_connections/components/query_permissions.tsx b/public/components/datasources/components/manage/query_permissions.tsx similarity index 88% rename from public/components/data_connections/components/query_permissions.tsx rename to public/components/datasources/components/manage/query_permissions.tsx index eee8312a8..92ae3ae95 100644 --- a/public/components/data_connections/components/query_permissions.tsx +++ b/public/components/datasources/components/manage/query_permissions.tsx @@ -17,11 +17,11 @@ import { OPENSEARCH_DOCUMENTATION_URL, QUERY_ALL, QUERY_RESTRICTED, -} from '../../../../common/constants/data_connections'; -import { PermissionsConfigurationProps } from '../../../../common/types/data_connections'; +} from '../../../../../common/constants/data_connections'; +import { PermissionsConfigurationProps } from '../../../../../common/types/data_connections'; export const QueryPermissionsConfiguration = (props: PermissionsConfigurationProps) => { - const { roles, selectedRoles, setSelectedRoles } = props; + const { roles, selectedRoles, setSelectedRoles, layout } = props; const [selectedAccessLevel, setSelectedAccessLevel] = useState( selectedRoles.length ? QUERY_RESTRICTED : QUERY_ALL @@ -59,7 +59,7 @@ export const QueryPermissionsConfiguration = (props: PermissionsConfigurationPro return ( - + Query Permissions diff --git a/public/components/datasources/components/new/configure_datasource.tsx b/public/components/datasources/components/new/configure_datasource.tsx new file mode 100644 index 000000000..004611d34 --- /dev/null +++ b/public/components/datasources/components/new/configure_datasource.tsx @@ -0,0 +1,191 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiPageBody, + EuiSpacer, + EuiText, + EuiButton, + EuiSteps, + EuiPageSideBar, + EuiBottomBar, + EuiButtonEmpty, +} from '@elastic/eui'; +import React, { useCallback, useEffect, useState } from 'react'; +import { ConfigureS3Datasource } from './configure_s3_datasource'; +import { coreRefs } from '../../../../../public/framework/core_refs'; +import { DATACONNECTIONS_BASE } from '../../../../../common/constants/shared'; +import { ReviewS3Datasource } from './review_s3_datasource_configuration'; +import { useToast } from '../../../../../public/components/common/toast'; +import { DatasourceType, Role } from '../../../../../common/types/data_connections'; + +interface ConfigureDatasourceProps { + type: string; +} + +export function Configure(props: ConfigureDatasourceProps) { + const { type } = props; + const { http } = coreRefs; + const { setToast } = useToast(); + + const [name, setName] = useState(''); + const [details, setDetails] = useState(''); + const [arn, setArn] = useState(''); + const [store, setStore] = useState(''); + const [roles, setRoles] = useState([]); + const [selectedQueryPermissionRoles, setSelectedQueryPermissionRoles] = useState([]); + const [page, setPage] = useState<'configure' | 'review'>('configure'); + const ConfigureDatasourceSteps = [ + { + title: 'Configure Data Source', + children: null, + }, + { + title: 'Review Configuration', + children: null, + }, + ]; + + useEffect(() => { + http!.get('/api/v1/configuration/roles').then((data) => + setRoles( + Object.keys(data.data).map((key) => { + return { label: key }; + }) + ) + ); + }, []); + + const ConfigureDatasource = (configurationProps: { datasourceType: DatasourceType }) => { + const { datasourceType } = configurationProps; + switch (datasourceType) { + case 'S3GLUE': + return ( + + ); + default: + return <>; + } + }; + + const ReviewDatasourceConfiguration = (configurationProps: { datasourceType: string }) => { + const { datasourceType } = configurationProps; + switch (datasourceType) { + case 'S3': + return ( + setPage('configure')} + /> + ); + default: + return <>; + } + }; + + const ReviewSaveOrCancel = useCallback(() => { + return ( + + + + { + window.location.hash = '#/new'; + }} + color="ghost" + size="s" + iconType="cross" + > + Cancel + + + + (page === 'review' ? setPage('configure') : {})} + color="ghost" + size="s" + iconType="arrowLeft" + > + Previous + + + + (page === 'review' ? createDatasource() : setPage('review'))} + size="s" + iconType="arrowRight" + fill + > + {page === 'configure' ? `Review Configuration` : `Connect to ${type}`} + + + + + ); + }, [page]); + + const createDatasource = () => { + http! + .post(`${DATACONNECTIONS_BASE}`, { + body: JSON.stringify({ + name, + allowedRoles: selectedQueryPermissionRoles.map((role) => role.label), + connector: 's3glue', + properties: { + 'glue.auth.type': 'iam_role', + 'glue.auth.role_arn': arn, + 'glue.indexstore.opensearch.uri': store, + 'glue.indexstore.opensearch.auth': false, + 'glue.indexstore.opensearch.region': 'us-west-2', + }, + }), + }) + .then(() => { + setToast(`Data source ${name} created`, 'success'); + window.location.hash = '#/manage'; + }) + .catch((err) => { + setToast(`Data source ${name} created`, 'success'); + window.location.hash = '#/manage'; + }); + }; + + return ( + + + + + + {page === 'configure' ? ( + + ) : ( + + )} + + + + + + ); +} diff --git a/public/components/datasources/components/new/configure_s3_datasource.tsx b/public/components/datasources/components/new/configure_s3_datasource.tsx new file mode 100644 index 000000000..803249aca --- /dev/null +++ b/public/components/datasources/components/new/configure_s3_datasource.tsx @@ -0,0 +1,218 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiPanel, + EuiTitle, + EuiSpacer, + EuiText, + EuiLink, + EuiFormRow, + EuiFieldText, + EuiTextArea, + EuiButton, + EuiSelect, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { OPENSEARCH_DOCUMENTATION_URL } from '../../../../../common/constants/data_connections'; +import { QueryPermissionsConfiguration } from '../manage/query_permissions'; +import { Role } from '../../../../../common/types/data_connections'; + +interface ConfigureS3DatasourceProps { + roles: Role[]; + selectedQueryPermissionRoles: Role[]; + setSelectedQueryPermissionRoles: React.Dispatch>; + currentName: string; + currentDetails: string; + currentArn: string; + currentStore: string; + setStoreForRequest: React.Dispatch>; + setNameForRequest: React.Dispatch>; + setDetailsForRequest: React.Dispatch>; + setArnForRequest: React.Dispatch>; +} + +export const ConfigureS3Datasource = (props: ConfigureS3DatasourceProps) => { + const { + setNameForRequest, + setDetailsForRequest, + setArnForRequest, + setStoreForRequest, + currentStore, + currentName, + currentDetails, + currentArn, + roles, + selectedQueryPermissionRoles, + setSelectedQueryPermissionRoles, + } = props; + + const [name, setName] = useState(currentName); + const [details, setDetails] = useState(currentDetails); + const [arn, setArn] = useState(currentArn); + const [store, setStore] = useState(currentStore); + const authOptions = [ + { value: 'option_one', text: 'No authentication' }, + { value: 'option_two', text: 'SIGV4' }, + { value: 'option_three', text: 'Basic Auth' }, + ]; + const [selectedAuthOption, setSelectedAuthOption] = useState(authOptions[0].value); + + return ( +
+ + +

{`Configure S3 Data Source`}

+
+ + + {`Connect to S3with OpenSearch and OpenSearch Dashboards `} + + Learn more + + + + +

Data source details

+
+ + + <> + +

+ This is the name the connection will be referenced by in OpenSearch Dashboards. It + is recommended to make this short yet descriptive to help users when selecting a + connection. +

+
+ { + setName(e.target.value); + }} + onBlur={(e) => { + setNameForRequest(e.target.value); + }} + /> + +
+ + { + setDetailsForRequest(e.target.value); + }} + onChange={(e) => { + setDetails(e.target.value); + }} + /> + + + + +

Glue authentication details

+
+ + + + <> + +

+ This parameters provides the authentication type information required for execution + engine to connect to glue. +

+
+ + +
+ + + <> + +

This should be the IAM role ARN

+
+ { + setArn(e.target.value); + }} + onBlur={(e) => { + setArnForRequest(e.target.value); + }} + /> + +
+ + + + +

Glue index store details

+
+ + + + <> + +

+ This parameters provides the OpenSearch cluster host information for glue. This + OpenSearch instance is used for writing index data back. +

+
+ { + setStore(e.target.value); + }} + onBlur={(e) => { + setStoreForRequest(e.target.value); + }} + /> + +
+ + + <> + +

Authentication settings to access the index store.

+
+ { + setSelectedAuthOption(e.target.value); + }} + /> + +
+ + + <> + +

The region where the index store is.

+
+ + +
+ + + + +
+
+ ); +}; diff --git a/public/components/datasources/components/new/new_datasource.tsx b/public/components/datasources/components/new/new_datasource.tsx new file mode 100644 index 000000000..669d0705e --- /dev/null +++ b/public/components/datasources/components/new/new_datasource.tsx @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiPage, EuiPageBody } from '@elastic/eui'; +import React, { useEffect, useState } from 'react'; +import { DataConnectionsHeader } from '../data_connections_header'; +import { HomeProps } from '../../home'; +import { NewDatasourceCardView } from './new_datasource_card_view'; + +export const NewDatasource = (props: HomeProps) => { + const { chrome } = props; + + // TODO: implement searching the card view with this + const [query, setQuery] = useState(''); + const [isCardView, setCardView] = useState(true); + + useEffect(() => { + chrome.setBreadcrumbs([ + { + text: 'Data sources', + href: '#/', + }, + ]); + }, []); + + // TODO: implement table view + const NewDatasourceTableView = () => { + return null; + }; + + return ( + + + + {isCardView ? : } + + + ); +}; diff --git a/public/components/datasources/components/new/new_datasource_card_view.tsx b/public/components/datasources/components/new/new_datasource_card_view.tsx new file mode 100644 index 000000000..04ede43aa --- /dev/null +++ b/public/components/datasources/components/new/new_datasource_card_view.tsx @@ -0,0 +1,115 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiPanel, + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiFieldSearch, + EuiButtonGroup, + EuiIcon, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { NewDatasourceDescription } from './new_datasource_description'; +import s3Svg from '../../icons/s3-logo.svg'; +import { DatasourceType } from '../../../../../common/types/data_connections'; + +export interface DatasourceCard { + name: DatasourceType; + displayName: string; + description: string; + displayIcon: JSX.Element; +} + +const Datasources: DatasourceCard[] = [ + { + name: 'OPENSEARCH', + displayName: 'OpenSearch', + description: 'Connect to self managed OpenSearch clusters', + displayIcon: , + }, + { + name: 'S3GLUE', + displayName: 'S3', + description: 'Connect to Amazon S3 via Amazon Glue', + displayIcon: , + }, +]; + +export function NewDatasourceCardView() { + const [toggleIconIdSelected, setToggleIconIdSelected] = useState('1'); + + const toggleButtonsIcons = [ + { + id: '0', + label: 'list', + iconType: 'list', + }, + { + id: '1', + label: 'grid', + iconType: 'grid', + }, + ]; + + const onChangeIcons = (optionId: string) => { + setToggleIconIdSelected(optionId); + }; + + const renderRows = (datasources: DatasourceCard[]) => { + return ( + <> + + {datasources.map((i, v) => { + return ( + + (window.location.hash = `#/configure/${i.name}`)} + /> + + ); + })} + + + + ); + }; + + return ( + + + + + {}} + /> + + + onChangeIcons(id)} + isIconOnly + /> + + + + {renderRows(Datasources)} + + ); +} diff --git a/public/components/datasources/components/new/new_datasource_description.tsx b/public/components/datasources/components/new/new_datasource_description.tsx new file mode 100644 index 000000000..91f13c666 --- /dev/null +++ b/public/components/datasources/components/new/new_datasource_description.tsx @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiSpacer, EuiText, EuiTitle, EuiHorizontalRule } from '@elastic/eui'; +import React from 'react'; + +export const NewDatasourceDescription = () => { + return ( +
+ +

Create a new data source

+
+ + + + Connect to a compatible data source or compute engine to bring your data into OpenSearch and + OpenSearch Dashboards. + + +
+ ); +}; diff --git a/public/components/datasources/components/new/review_s3_datasource_configuration.tsx b/public/components/datasources/components/new/review_s3_datasource_configuration.tsx new file mode 100644 index 000000000..2a65f8e2b --- /dev/null +++ b/public/components/datasources/components/new/review_s3_datasource_configuration.tsx @@ -0,0 +1,108 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiPanel, + EuiTitle, + EuiSpacer, + EuiText, + EuiFlexGroup, + EuiHorizontalRule, + EuiFlexItem, + EuiButton, +} from '@elastic/eui'; +import React from 'react'; + +interface ConfigureS3DatasourceProps { + selectedQueryPermissionRoles: Role[]; + currentName: string; + currentDetails: string; + currentArn: string; + currentStore: string; + goBack: () => void; +} + +export const ReviewS3Datasource = (props: ConfigureS3DatasourceProps) => { + const { + currentStore, + currentName, + currentDetails, + currentArn, + selectedQueryPermissionRoles, + goBack, + } = props; + + return ( +
+ + +

{`Review S3 Data Source Configuration`}

+
+ + + + + +

Data source configuration

+
+
+ + Edit + +
+ + + + + + + Data source name + + {currentName} + + + + Description + + {currentDetails} + + + + + + + + Glue authentication ARN + + {currentArn} + + + + Glue index store URI + + {currentStore} + + + + + + + + Query Permissions + + {selectedQueryPermissionRoles && selectedQueryPermissionRoles.length + ? `Restricted - ${selectedQueryPermissionRoles + .map((role) => role.label) + .join(',')}` + : 'Everyone'} + + + + + +
+
+ ); +}; diff --git a/public/components/data_connections/components/no_access.tsx b/public/components/datasources/components/no_access.tsx similarity index 100% rename from public/components/data_connections/components/no_access.tsx rename to public/components/datasources/components/no_access.tsx diff --git a/public/components/data_connections/components/save_or_cancel.tsx b/public/components/datasources/components/save_or_cancel.tsx similarity index 100% rename from public/components/data_connections/components/save_or_cancel.tsx rename to public/components/datasources/components/save_or_cancel.tsx diff --git a/public/components/data_connections/home.tsx b/public/components/datasources/home.tsx similarity index 64% rename from public/components/data_connections/home.tsx rename to public/components/datasources/home.tsx index 29d5e83bd..fef07a780 100644 --- a/public/components/data_connections/home.tsx +++ b/public/components/datasources/home.tsx @@ -6,8 +6,10 @@ import React from 'react'; import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom'; import { ChromeBreadcrumb, ChromeStart, HttpStart } from '../../../../../src/core/public'; -import { DataConnection } from './components/data_connection'; -import { ManageDataConnectionsTable } from './components/manage_data_connections_table'; +import { DataConnection } from './components/manage/data_connection'; +import { ManageDataConnectionsTable } from './components/manage/manage_data_connections_table'; +import { NewDatasource } from './components/new/new_datasource'; +import { Configure } from './components/new/configure_datasource'; export interface HomeProps extends RouteComponentProps { pplService: any; @@ -44,6 +46,15 @@ export const Home = (props: HomeProps) => { path={['/', '/manage']} render={(routerProps) => } /> + } /> + + ( + + )} + /> ); diff --git a/public/components/datasources/icons/s3-logo.svg b/public/components/datasources/icons/s3-logo.svg new file mode 100644 index 000000000..39b0fc099 --- /dev/null +++ b/public/components/datasources/icons/s3-logo.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/server/adaptors/ppl_plugin.ts b/server/adaptors/ppl_plugin.ts index 563c43672..6007f913f 100644 --- a/server/adaptors/ppl_plugin.ts +++ b/server/adaptors/ppl_plugin.ts @@ -68,6 +68,14 @@ export const PPLPlugin = function (Client, config, components) { method: 'DELETE', }); + ppl.createDataSource = ca({ + url: { + fmt: `${OPENSEARCH_DATACONNECTIONS_API.DATACONNECTION}`, + }, + needBody: true, + method: 'POST', + }); + ppl.modifyDataConnection = ca({ url: { fmt: `${OPENSEARCH_DATACONNECTIONS_API.DATACONNECTION}`, diff --git a/server/routes/data_connections/data_connections_router.ts b/server/routes/data_connections/data_connections_router.ts index a65660ba4..52dc1c284 100644 --- a/server/routes/data_connections/data_connections_router.ts +++ b/server/routes/data_connections/data_connections_router.ts @@ -103,6 +103,43 @@ export function registerDataConnectionsRoute(router: IRouter) { } ); + router.post( + { + path: `${DATACONNECTIONS_BASE}`, + validate: { + body: schema.object({ + name: schema.string(), + connector: schema.string(), + allowedRoles: schema.arrayOf(schema.string()), + properties: schema.any(), + }), + }, + }, + async (context, request, response) => { + try { + const dataConnectionsresponse = await context.observability_plugin.observabilityClient + .asScoped(request) + .callAsCurrentUser('ppl.createDataSource', { + body: { + name: request.body.name, + connector: request.body.connector, + allowedRoles: request.body.allowedRoles, + properties: request.body.properties, + }, + }); + return response.ok({ + body: dataConnectionsresponse, + }); + } catch (error: any) { + console.error('Issue in creating data source:', error); + return response.custom({ + statusCode: error.statusCode || 500, + body: error.message, + }); + } + } + ); + router.get( { path: `${DATACONNECTIONS_BASE}`,