Skip to content

Commit

Permalink
[MD] Datasource Management - creation & listing - UI only (#2128)
Browse files Browse the repository at this point in the history
* data source management - creation & Listing UI only

* data source management - creation & Listing UI only

* Create/edit data source feature

* toggling default value

* refactoring code as per review comments

* toggling server flag to false

Signed-off-by: mpabba3003 <[email protected]>
  • Loading branch information
mpabba3003 authored Aug 17, 2022
1 parent d38cb85 commit bae7143
Show file tree
Hide file tree
Showing 20 changed files with 1,297 additions and 110 deletions.
2 changes: 2 additions & 0 deletions src/plugins/data_source_management/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@

export const PLUGIN_ID = 'dataSourceManagement';
export const PLUGIN_NAME = 'Data Sources';
export const MODE_CREATE = 'Create Data Source';
export const MODE_EDIT = 'Edit Data Source';
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "opensearchDashboards",
"server": false,
"ui": true,
"requiredPlugins": ["management"],
"requiredPlugins": ["management", "credentialManagement"],

This comment has been minimized.

Copy link
@noCharger

noCharger Aug 17, 2022

Contributor

Do we need CM plugin as a dependency of DSM?

"optionalPlugins": [],
"requiredBundles": ["opensearchDashboardsReact"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,37 @@
*/

import { i18n } from '@osd/i18n';
import { DataSourceEditPageItem } from '../types';

export function getListBreadcrumbs() {
return [
{
text: i18n.translate('indexPatternManagement.dataSources.listBreadcrumb', {
text: i18n.translate('dataSourcesManagement.dataSources.listBreadcrumb', {
defaultMessage: 'Data Sources',
}),
href: `/`,
},
];
}

export function getCreateBreadcrumbs() {
return [
...getListBreadcrumbs(),
{
text: i18n.translate('dataSourcesManagement.dataSources.createBreadcrumb', {
defaultMessage: 'Create data source',
}),
href: `/create`,
},
];
}

export function getEditBreadcrumbs(dataSource: DataSourceEditPageItem) {
return [
...getListBreadcrumbs(),
{
text: dataSource.title,
href: `/${dataSource.id}`,
},
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { History } from 'history';

import { EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@osd/i18n/react';

interface Props {
history: History;
}

export const CreateButton = ({ history }: Props) => {
return (
<EuiButton
data-test-subj="createDataSourceButton"
fill={true}
onClick={() => history.push('/create')}
iconType="plusInCircle"
>
<FormattedMessage
id="dataSourcesManagement.dataSourcesTable.createBtn"
defaultMessage="Create Data Source"
/>
</EuiButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { CreateButton } from './create_button';
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,112 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiTitle } from '@elastic/eui';
import React from 'react';
import { withRouter } from 'react-router-dom';
import { EuiGlobalToastList, EuiGlobalToastListToast } from '@elastic/eui';
import React, { useState } from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { useEffectOnce } from 'react-use';
import { FormattedMessage } from '@osd/i18n/react';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { DataSourceEditPageItem, DataSourceManagementContext, ToastMessageItem } from '../../types';
import { getCreateBreadcrumbs } from '../breadcrumbs';
import { CreateEditDataSourceWizard } from '../create_edit_data_source_wizard';
import { MODE_CREATE } from '../../../common';
import { createSingleDataSource } from '../utils';

type CreateDataSourceWizardProps = RouteComponentProps;

const CreateDataSourceWizard: React.FunctionComponent<CreateDataSourceWizardProps> = (
props: CreateDataSourceWizardProps
) => {
/* Initialization */
const { savedObjects, setBreadcrumbs } = useOpenSearchDashboards<
DataSourceManagementContext
>().services;

const toastLifeTimeMs: number = 6000;

/* State Variables */
const [toasts, setToasts] = useState<EuiGlobalToastListToast[]>([]);

/* Set breadcrumb */
useEffectOnce(() => {
setBreadcrumbs(getCreateBreadcrumbs());
});

/* Handle submit - create data source*/
const handleSubmit = async ({
title,
description,
endpoint,
credentialId,
noAuthentication,
}: DataSourceEditPageItem) => {
try {
// TODO: Add rendering spinner

const references = [];
const attributes = { title, description, endpoint };

if (credentialId) {
references.push({ id: credentialId, type: 'credential', name: 'credential' });
}
const options = { references };

await createSingleDataSource(savedObjects.client, attributes, options);

props.history.push('');
} catch (e) {
handleDisplayToastMessage({
id: 'dataSourcesManagement.createDataSource.createDataSourceFailMsg',
defaultMessage: 'Creation of the Data Source failed with some errors. Please try it again',
color: 'warning',
iconType: 'alert',
});
}
};

const handleDisplayToastMessage = ({ id, defaultMessage, color, iconType }: ToastMessageItem) => {
if (id && defaultMessage && color && iconType) {
const failureMsg = <FormattedMessage id={id} defaultMessage={defaultMessage} />;
setToasts([
...toasts,
{
title: failureMsg,
id: failureMsg.props.id,
color,
iconType,
},
]);
}
};

/* Render the creation wizard */
const renderContent = () => {
return (
<CreateEditDataSourceWizard
wizardMode={MODE_CREATE}
handleSubmit={handleSubmit}
displayToastMessage={handleDisplayToastMessage}
/>
);
};

/* Remove toast on dismiss*/
const removeToast = (id: string) => {
setToasts(toasts.filter((toast) => toast.id !== id));
};

export const CreateDataSourceWizard = () => {
return (
<EuiTitle>
<h2>{'This is the data source creation page'}</h2>
</EuiTitle>
<>
{renderContent()}
<EuiGlobalToastList
toasts={toasts}
dismissToast={({ id }) => {
removeToast(id);
}}
toastLifeTimeMs={toastLifeTimeMs}
/>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { CredentialsComboBoxItem } from '../../../../types';

interface CredentialsComboBoxProps {
selectedCredentials: CredentialsComboBoxItem[];
availableCredentials: CredentialsComboBoxItem[];
setSelectedCredentials: (selectedOptions: CredentialsComboBoxItem[]) => void;
}

export const CredentialsComboBox: React.FunctionComponent<CredentialsComboBoxProps> = ({
availableCredentials,
selectedCredentials,
setSelectedCredentials,
}: CredentialsComboBoxProps) => {
const onOptionsChanged = (options: EuiComboBoxOptionOption[]) => {
const opts = new Set();
const selectedCredentialsOptions: CredentialsComboBoxItem[] = [];
if (options?.length) {
options.forEach((rec) => {
opts.add(rec.id);
});

availableCredentials.forEach((cred: CredentialsComboBoxItem) => {
if (opts.has(cred.id)) {
selectedCredentialsOptions.push(cred);
}
});
}
setSelectedCredentials(selectedCredentialsOptions);
};

return (
<EuiComboBox
aria-label="Search for Stored Credential"
placeholder="Search for Stored Credential"
key="id"
options={availableCredentials}
singleSelection={true}
selectedOptions={selectedCredentials}
onChange={(options: EuiComboBoxOptionOption[]) => onOptionsChanged(options)}
isClearable={true}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { CredentialsComboBox } from './credentials_combo_box';
Loading

0 comments on commit bae7143

Please sign in to comment.