From 6a16c227dd83aacadd597a70534d37f3623e870e Mon Sep 17 00:00:00 2001 From: Louis Chu Date: Thu, 25 Aug 2022 00:06:36 -0500 Subject: [PATCH] [MD]Refactor layout and validate input fields for listing and create pages (#2202) Signed-off-by: Louis Chu Signed-off-by: Louis Chu Signed-off-by: Kristen Tian --- .../public/components/breadcrumbs.ts | 4 +- .../create_credential_form.tsx | 261 ++++++++++++++++++ .../create_credential_form/index.tsx | 6 + .../components/header/header.tsx | 13 +- .../components/header/index.tsx | 0 .../components/common/components/index.tsx | 6 + .../public/components/common/index.tsx | 7 + .../{ => common}/text_content/index.ts | 2 +- .../{ => common}/text_content/text_content.ts | 0 .../create_button/create_button.tsx | 2 +- .../create_credential_wizard.tsx | 181 +++--------- .../credential_table/credentials_table.tsx | 46 +-- .../edit_credential/edit_credential_page.tsx | 38 --- .../components}/edit_credential.tsx | 39 +-- .../components/index.tsx | 6 + .../edit_credential_wizard.tsx | 88 ++++++ .../index.tsx | 2 +- .../public/components/index.tsx | 2 +- .../public/components/types.ts | 19 +- .../public/components/utils.ts | 4 +- .../credential_management/public/types.ts | 7 + .../data_source/common/credentials/index.ts | 7 +- src/plugins/data_source/common/index.ts | 7 +- src/plugins/data_source/public/index.ts | 8 +- ...credential_saved_objects_client_wrapper.ts | 14 +- 25 files changed, 526 insertions(+), 243 deletions(-) create mode 100644 src/plugins/credential_management/public/components/common/components/create_credential_form/create_credential_form.tsx create mode 100644 src/plugins/credential_management/public/components/common/components/create_credential_form/index.tsx rename src/plugins/credential_management/public/components/{create_credential_wizard => common}/components/header/header.tsx (90%) rename src/plugins/credential_management/public/components/{create_credential_wizard => common}/components/header/index.tsx (100%) create mode 100644 src/plugins/credential_management/public/components/common/components/index.tsx create mode 100644 src/plugins/credential_management/public/components/common/index.tsx rename src/plugins/credential_management/public/components/{ => common}/text_content/index.ts (57%) rename src/plugins/credential_management/public/components/{ => common}/text_content/text_content.ts (100%) delete mode 100644 src/plugins/credential_management/public/components/edit_credential/edit_credential_page.tsx rename src/plugins/credential_management/public/components/{edit_credential => edit_credential_wizard/components}/edit_credential.tsx (87%) create mode 100644 src/plugins/credential_management/public/components/edit_credential_wizard/components/index.tsx create mode 100644 src/plugins/credential_management/public/components/edit_credential_wizard/edit_credential_wizard.tsx rename src/plugins/credential_management/public/components/{edit_credential => edit_credential_wizard}/index.tsx (94%) diff --git a/src/plugins/credential_management/public/components/breadcrumbs.ts b/src/plugins/credential_management/public/components/breadcrumbs.ts index b4a416eb152c..e44f840b009f 100644 --- a/src/plugins/credential_management/public/components/breadcrumbs.ts +++ b/src/plugins/credential_management/public/components/breadcrumbs.ts @@ -4,7 +4,7 @@ */ import { i18n } from '@osd/i18n'; -import { CredentialEditPageItem } from './types'; +import { EditCredentialItem } from './types'; export function getListBreadcrumbs() { return [ @@ -29,7 +29,7 @@ export function getCreateBreadcrumbs() { ]; } -export function getEditBreadcrumbs(credential: CredentialEditPageItem) { +export function getEditBreadcrumbs(credential: EditCredentialItem) { return [ ...getListBreadcrumbs(), { diff --git a/src/plugins/credential_management/public/components/common/components/create_credential_form/create_credential_form.tsx b/src/plugins/credential_management/public/components/common/components/create_credential_form/create_credential_form.tsx new file mode 100644 index 000000000000..592a8d9dc524 --- /dev/null +++ b/src/plugins/credential_management/public/components/common/components/create_credential_form/create_credential_form.tsx @@ -0,0 +1,261 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; + +import { + EuiForm, + EuiFormRow, + EuiFieldText, + EuiButton, + EuiFieldPassword, + EuiPageContent, + EuiHorizontalRule, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage } from '@osd/i18n/react'; +import { DocLinksStart } from 'src/core/public'; +import { context as contextType } from '../../../../../../opensearch_dashboards_react/public'; + +import { CredentialManagmentContextValue } from '../../../../types'; +import { CreateCredentialItem } from '../../../types'; +import { Header } from '../header'; + +export interface CredentialFormProps { + docLinks: DocLinksStart; + handleSubmit: (formValues: CreateCredentialItem) => void; +} + +export interface CredentialFormState { + formErrors: string[]; + formErrorsByField: CredentialFormValidation; + title: string; + description: string; + username: string; + password: string; + dual: boolean; +} + +interface CredentialFormValidation { + title: string[]; + description: string[]; + username: string[]; + password: string[]; +} + +const defaultValidation: CredentialFormValidation = { + title: [], + description: [], + username: [], + password: [], +}; + +export class CredentialForm extends React.Component { + static contextType = contextType; + public readonly context!: CredentialManagmentContextValue; + + constructor(props: CredentialFormProps, context: CredentialManagmentContextValue) { + super(props, context); + + this.state = { + formErrors: [], + formErrorsByField: { ...defaultValidation }, + title: '', + description: '', + username: '', + password: '', + dual: true, + }; + } + + /* Validations */ + + isFormValid = () => { + const validationByField: CredentialFormValidation = { + title: [], + description: [], + username: [], + password: [], + }; + const formErrorMessages: string[] = []; + /* Title validation */ + if (!this.state.title) { + validationByField.title.push('Title should not be empty'); + formErrorMessages.push('Title should not be empty'); + } + /* Username Validation */ + if (!this.state.username) { + validationByField.username.push('Username should not be empty'); + formErrorMessages.push('Username should not be empty'); + } + /* Password Validation */ + if (!this.state.password) { + validationByField.password.push('Password should not be empty'); + formErrorMessages.push('Password should not be empty'); + } + + this.setState({ + formErrors: formErrorMessages, + formErrorsByField: { ...validationByField }, + }); + return formErrorMessages.length === 0; + }; + + /* Events */ + + onChangeTitle = (e: { target: { value: any } }) => { + this.setState({ title: e.target.value }, () => { + if (this.state.formErrorsByField.title.length) { + this.isFormValid(); + } + }); + }; + + onChangeDescription = (e: { target: { value: any } }) => { + this.setState({ description: e.target.value }, () => { + if (this.state.formErrorsByField.description.length) { + this.isFormValid(); + } + }); + }; + + onChangeUsername = (e: { target: { value: any } }) => { + this.setState({ username: e.target.value }, () => { + if (this.state.formErrorsByField.username.length) { + this.isFormValid(); + } + }); + }; + + onChangePassword = (e: { target: { value: any } }) => { + this.setState({ password: e.target.value }, () => { + if (this.state.formErrorsByField.password.length) { + this.isFormValid(); + } + }); + }; + + onClickSubmitForm = () => { + if (this.isFormValid()) { + const formValues: CreateCredentialItem = { + title: this.state.title, + description: this.state.description, + username: this.state.username, + password: this.state.password, + }; + this.props.handleSubmit(formValues); + } + }; + + /* Render methods */ + + renderHeader() { + const { docLinks } = this.props; + return
; + } + + renderContent = () => { + const header = this.renderHeader(); + + return ( + + {header} + + + {/* Title */} + + + + + {/* Description */} + + + + + {/* Authentication */} + + +

+ +

+
+ + + {/* Username */} + + + + + {/* Password */} + + + + + + + {/* Create Credential button*/} + + Create stored credential + +
+
+ ); + }; + + render() { + return <>{this.renderContent()}; + } +} diff --git a/src/plugins/credential_management/public/components/common/components/create_credential_form/index.tsx b/src/plugins/credential_management/public/components/common/components/create_credential_form/index.tsx new file mode 100644 index 000000000000..aad800a142c2 --- /dev/null +++ b/src/plugins/credential_management/public/components/common/components/create_credential_form/index.tsx @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { CredentialForm } from './create_credential_form'; diff --git a/src/plugins/credential_management/public/components/create_credential_wizard/components/header/header.tsx b/src/plugins/credential_management/public/components/common/components/header/header.tsx similarity index 90% rename from src/plugins/credential_management/public/components/create_credential_wizard/components/header/header.tsx rename to src/plugins/credential_management/public/components/common/components/header/header.tsx index 07df8ca6e58a..95cccfa99c2d 100644 --- a/src/plugins/credential_management/public/components/create_credential_wizard/components/header/header.tsx +++ b/src/plugins/credential_management/public/components/common/components/header/header.tsx @@ -9,14 +9,21 @@ import { EuiBetaBadge, EuiSpacer, EuiTitle, EuiText, EuiCode, EuiLink } from '@e import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; -import { DocLinksStart } from 'opensearch-dashboards/public'; +import { DocLinksStart } from 'src/core/public'; import { useOpenSearchDashboards } from '../../../../../../opensearch_dashboards_react/public'; import { CredentialManagementContext } from '../../../../types'; -export const Header = ({ docLinks }: { docLinks: DocLinksStart }) => { + +export const Header = ({ + prompt, + docLinks, +}: { + prompt?: React.ReactNode; + docLinks: DocLinksStart; +}) => { const changeTitle = useOpenSearchDashboards().services.chrome .docTitle.change; const createCredentialHeader = i18n.translate('credentialManagement.createIndexPatternHeader', { - defaultMessage: 'Save Your Credential', + defaultMessage: 'Create Stored Credential', }); changeTitle(createCredentialHeader); diff --git a/src/plugins/credential_management/public/components/create_credential_wizard/components/header/index.tsx b/src/plugins/credential_management/public/components/common/components/header/index.tsx similarity index 100% rename from src/plugins/credential_management/public/components/create_credential_wizard/components/header/index.tsx rename to src/plugins/credential_management/public/components/common/components/header/index.tsx diff --git a/src/plugins/credential_management/public/components/common/components/index.tsx b/src/plugins/credential_management/public/components/common/components/index.tsx new file mode 100644 index 000000000000..aad800a142c2 --- /dev/null +++ b/src/plugins/credential_management/public/components/common/components/index.tsx @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { CredentialForm } from './create_credential_form'; diff --git a/src/plugins/credential_management/public/components/common/index.tsx b/src/plugins/credential_management/public/components/common/index.tsx new file mode 100644 index 000000000000..835d1e36073c --- /dev/null +++ b/src/plugins/credential_management/public/components/common/index.tsx @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { CredentialForm } from './components'; +export { LocalizedContent } from './text_content'; diff --git a/src/plugins/credential_management/public/components/text_content/index.ts b/src/plugins/credential_management/public/components/common/text_content/index.ts similarity index 57% rename from src/plugins/credential_management/public/components/text_content/index.ts rename to src/plugins/credential_management/public/components/common/text_content/index.ts index 4ac019150c2a..eefe68f2d628 100644 --- a/src/plugins/credential_management/public/components/text_content/index.ts +++ b/src/plugins/credential_management/public/components/common/text_content/index.ts @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export * as localizedContent from '../text_content/text_content'; +export * as LocalizedContent from '../text_content/text_content'; diff --git a/src/plugins/credential_management/public/components/text_content/text_content.ts b/src/plugins/credential_management/public/components/common/text_content/text_content.ts similarity index 100% rename from src/plugins/credential_management/public/components/text_content/text_content.ts rename to src/plugins/credential_management/public/components/common/text_content/text_content.ts diff --git a/src/plugins/credential_management/public/components/create_button/create_button.tsx b/src/plugins/credential_management/public/components/create_button/create_button.tsx index 41b580fcf8c8..3f06c9a5d767 100644 --- a/src/plugins/credential_management/public/components/create_button/create_button.tsx +++ b/src/plugins/credential_management/public/components/create_button/create_button.tsx @@ -23,7 +23,7 @@ export const CreateButton = ({ history }: Props) => { > ); diff --git a/src/plugins/credential_management/public/components/create_credential_wizard/create_credential_wizard.tsx b/src/plugins/credential_management/public/components/create_credential_wizard/create_credential_wizard.tsx index 55d24ddb8687..a37b93f4a322 100644 --- a/src/plugins/credential_management/public/components/create_credential_wizard/create_credential_wizard.tsx +++ b/src/plugins/credential_management/public/components/create_credential_wizard/create_credential_wizard.tsx @@ -7,35 +7,23 @@ import React from 'react'; import { FormattedMessage } from '@osd/i18n/react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { - EuiHorizontalRule, EuiGlobalToastList, EuiGlobalToastListToast, - EuiForm, - EuiDescribedFormGroup, - EuiFormRow, - EuiFieldText, - EuiSelect, - EuiLink, - EuiButton, - EuiPageContent, - EuiFieldPassword, EuiLoadingSpinner, EuiOverlayMask, } from '@elastic/eui'; import { DocLinksStart } from 'src/core/public'; -import { Credential } from '../../../../data_source/public'; import { getCreateBreadcrumbs } from '../breadcrumbs'; import { CredentialManagmentContextValue } from '../../types'; -import { Header } from './components/header'; import { context as contextType } from '../../../../opensearch_dashboards_react/public'; -interface CreateCredentialWizardState { - credentialName?: string; - credentialMaterialsType?: string; - username?: string; - password?: string; - dual: boolean; +import { CredentialForm } from '../common'; +import { CreateCredentialItem } from '../types'; + +import { CredentialMaterialsType } from '../../../../data_source/public'; + +export interface CreateCredentialWizardState { toasts: EuiGlobalToastListToast[]; docLinks: DocLinksStart; isLoading: boolean; @@ -47,152 +35,40 @@ export class CreateCredentialWizard extends React.Component< > { static contextType = contextType; public readonly context!: CredentialManagmentContextValue; + constructor(props: RouteComponentProps, context: CredentialManagmentContextValue) { super(props, context); context.services.setBreadcrumbs(getCreateBreadcrumbs()); this.state = { - credentialName: undefined, - credentialMaterialsType: undefined, - username: undefined, - password: undefined, - dual: true, toasts: [], docLinks: context.services.docLinks, isLoading: false, }; } - // TODO: Fix header component error https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2048 - renderHeader() { - const { docLinks } = this.state; - - return
; - } - - renderContent() { - const header = this.renderHeader(); - - const options = [ - { value: undefined, text: 'Select Credential Materials Type' }, - { - value: Credential.CredentialMaterialsType.UsernamePasswordType, - text: 'Username and Password Credential', - }, - ]; - - return ( - - {header} - - - Credential Name} - description={

The name of credential that you want to create

} - > - - this.setState({ credentialName: e.target.value })} - /> - -
- Credential Type} - description={ -
-

- The type of credential that you want to create{' '} - - Credential Types Supported - -

-
    -
  • - For username_password_credential type: this type can be used for{' '} - credentials in format of username, password.{' '} -
  • -
  • Ex: OpenSearch basic auth
  • -
-
    -
  • - For aws_iam_credential type: this type can only be used for aws iam - credential, with aws_access_key_id, aws_secret_access_key, and region (optional) -
  • -
-
- } - > - - this.setState({ credentialMaterialsType: e.target.value })} - options={options} - /> - - - this.setState({ username: e.target.value })} - /> - - - this.setState({ password: e.target.value })} - /> - -
- - Save - -
-
- ); - } - + /* Events */ + /* Remove toast on dismiss */ removeToast = (id: string) => { this.setState((prevState) => ({ toasts: prevState.toasts.filter((toast) => toast.id !== id), })); }; - render() { - const content = this.renderContent(); - const { isLoading } = this.state; - return isLoading ? ( - - - - ) : ( - <> - {content} - { - this.removeToast(id); - }} - toastLifeTimeMs={6000} - /> - - ); - } - - createCredential = async () => { + /* Create credential on form submit */ + createCredential = async ({ title, description, username, password }: CreateCredentialItem) => { const { savedObjects } = this.context.services; this.setState({ isLoading: true }); try { await savedObjects.client.create('credential', { - title: this.state.credentialName, + title, + description, credentialMaterials: { - credentialMaterialsType: this.state.credentialMaterialsType, + credentialMaterialsType: CredentialMaterialsType.UsernamePasswordType, credentialMaterialsContent: { - username: this.state.username, - password: this.state.password, + username, + password, }, }, }); @@ -217,6 +93,31 @@ export class CreateCredentialWizard extends React.Component< } this.setState({ isLoading: false }); }; + + /* Render methods */ + /* Render the creation wizard */ + render() { + const { isLoading } = this.state; + return isLoading ? ( + + + + ) : ( + <> + + { + this.removeToast(id); + }} + toastLifeTimeMs={6000} + /> + + ); + } } export const CreateCredentialWizardWithRouter = withRouter(CreateCredentialWizard); diff --git a/src/plugins/credential_management/public/components/credential_table/credentials_table.tsx b/src/plugins/credential_management/public/components/credential_table/credentials_table.tsx index f195d2942b97..9d51bbe21d64 100644 --- a/src/plugins/credential_management/public/components/credential_table/credentials_table.tsx +++ b/src/plugins/credential_management/public/components/credential_table/credentials_table.tsx @@ -39,7 +39,7 @@ import { import { getListBreadcrumbs } from '../breadcrumbs'; import { CredentialManagementContext } from '../../types'; import { deleteCredentials, getCredentials } from '../utils'; -import { localizedContent } from '../text_content'; +import { LocalizedContent } from '../common'; import { CredentialsTableItem } from '../types'; import { CreateButton } from '../create_button'; @@ -55,17 +55,8 @@ const sorting = { }, }; -const search = { - box: { - incremental: true, - schema: { - fields: { title: { type: 'string' } }, - }, - }, -}; - const title = i18n.translate('credentialManagement.credentialsTable.title', { - defaultMessage: 'Credentials', + defaultMessage: 'Stored credentials', }); interface Props extends RouteComponentProps { @@ -96,7 +87,7 @@ export const CredentialsTable = ({ canSave, history }: Props) => { const columns = [ { field: 'title', - name: 'Credential Name', + name: 'Title', render: ( name: string, index: { @@ -123,9 +114,17 @@ export const CredentialsTable = ({ canSave, history }: Props) => { dataType: 'string' as const, sortable: ({ sort }: { sort: string }) => sort, }, + { + field: 'description', + name: 'Description', + truncateText: true, + mobileOptions: { + show: false, + }, + }, { field: 'credentialMaterialsType', - name: 'Credential Type', + name: 'Type', truncateText: true, mobileOptions: { show: false, @@ -142,6 +141,13 @@ export const CredentialsTable = ({ canSave, history }: Props) => { }; const renderDeleteButton = () => { + let deleteButtonMsg = 'Delete'; + + if (selectedCredentials.length === 1) { + deleteButtonMsg = `${deleteButtonMsg} ${selectedCredentials.length} Credential`; + } else if (selectedCredentials.length > 1) { + deleteButtonMsg = `${deleteButtonMsg} ${selectedCredentials.length} Credentials`; + } return ( { }} disabled={selectedCredentials.length === 0} > - Delete {selectedCredentials.length} Credential{selectedCredentials.length >= 2 ? 's' : ''} + {deleteButtonMsg} ); }; @@ -208,20 +214,20 @@ export const CredentialsTable = ({ canSave, history }: Props) => { const tableRenderDeleteModal = () => { return confirmDeleteVisible ? ( { setConfirmDeleteVisible(false); }} onConfirm={() => { onClickDelete(); }} - cancelButtonText={localizedContent.cancelButtonOnDeleteCancelText} - confirmButtonText={localizedContent.confirmButtonOnDeleteComfirmText} + cancelButtonText={LocalizedContent.cancelButtonOnDeleteCancelText} + confirmButtonText={LocalizedContent.confirmButtonOnDeleteComfirmText} defaultFocusedButton="confirm" > -

{localizedContent.deleteCredentialDescribeMsg}

-

{localizedContent.deleteCredentialConfirmMsg}

-

{localizedContent.deleteCredentialWarnMsg}

+

{LocalizedContent.deleteCredentialDescribeMsg}

+

{LocalizedContent.deleteCredentialConfirmMsg}

+

{LocalizedContent.deleteCredentialWarnMsg}

) : null; }; diff --git a/src/plugins/credential_management/public/components/edit_credential/edit_credential_page.tsx b/src/plugins/credential_management/public/components/edit_credential/edit_credential_page.tsx deleted file mode 100644 index 9e1d84728955..000000000000 --- a/src/plugins/credential_management/public/components/edit_credential/edit_credential_page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useEffect, useState } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; -import { CredentialManagementContext } from '../../types'; -import { getEditBreadcrumbs } from '../breadcrumbs'; -import { CredentialEditPageItem } from '../types'; -import { EditCredential } from './edit_credential'; - -const EditCredentialPage: React.FC> = ({ ...props }) => { - const { savedObjects, setBreadcrumbs } = useOpenSearchDashboards< - CredentialManagementContext - >().services; - const [credential, setCredential] = useState(); - useEffect(() => { - savedObjects.client.get('credential', props.match.params.id).then((savedObject) => { - const object = { - id: savedObject.id, - title: savedObject.attributes.title, - credentialMaterialsType: savedObject.attributes.credentialMaterials.credentialMaterialsType, - }; - setCredential(object); - setBreadcrumbs(getEditBreadcrumbs(object)); - }); - }, [savedObjects.client, props.match.params.id, setBreadcrumbs]); - - if (credential) { - return ; - } else { - return

Credential not found!

; - } -}; - -export const EditCredentialPageWithRouter = withRouter(EditCredentialPage); diff --git a/src/plugins/credential_management/public/components/edit_credential/edit_credential.tsx b/src/plugins/credential_management/public/components/edit_credential_wizard/components/edit_credential.tsx similarity index 87% rename from src/plugins/credential_management/public/components/edit_credential/edit_credential.tsx rename to src/plugins/credential_management/public/components/edit_credential_wizard/components/edit_credential.tsx index 33d3de315a91..4b036d7d2e87 100644 --- a/src/plugins/credential_management/public/components/edit_credential/edit_credential.tsx +++ b/src/plugins/credential_management/public/components/edit_credential_wizard/components/edit_credential.tsx @@ -27,14 +27,17 @@ import { EuiOverlayMask, } from '@elastic/eui'; import { DocLinksStart } from 'src/core/public'; -import { Credential } from '../../../../data_source/public'; +import { + CredentialMaterialsType, + CREDENTIAL_SAVED_OBJECT_TYPE, +} from '../../../../../data_source/public'; -import { getCreateBreadcrumbs } from '../breadcrumbs'; -import { CredentialManagmentContextValue } from '../../types'; +import { getCreateBreadcrumbs } from '../../breadcrumbs'; +import { CredentialManagmentContextValue } from '../../../types'; // TODO: Add Header https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2051 -import { context as contextType } from '../../../../opensearch_dashboards_react/public'; -import { CredentialEditPageItem } from '../types'; -import { localizedContent } from '../text_content'; +import { context as contextType } from '../../../../../opensearch_dashboards_react/public'; +import { EditCredentialItem } from '../../types'; +import { LocalizedContent } from '../../common/text_content'; interface EditCredentialState { credentialName: string; @@ -49,7 +52,7 @@ interface EditCredentialState { } export interface EditCredentialProps extends RouteComponentProps { - credential: CredentialEditPageItem; + credential: EditCredentialItem; } export class EditCredentialComponent extends React.Component< @@ -65,7 +68,7 @@ export class EditCredentialComponent extends React.Component< this.state = { credentialName: props.credential.title, - credentialMaterialsType: props.credential.credentialMaterialsType, + credentialMaterialsType: CredentialMaterialsType.UsernamePasswordType, username: undefined, password: undefined, dual: true, @@ -80,7 +83,7 @@ export class EditCredentialComponent extends React.Component< const { savedObjects } = this.context.services; this.setState({ isLoading: true }); try { - await savedObjects.client.delete('credential', this.props.credential.id); + await savedObjects.client.delete(CREDENTIAL_SAVED_OBJECT_TYPE, this.props.credential.id); this.props.history.push(''); } catch (e) { const deleteCredentialFailMsg = ( @@ -114,12 +117,12 @@ export class EditCredentialComponent extends React.Component< }} > - + @@ -127,18 +130,18 @@ export class EditCredentialComponent extends React.Component< {this.state.isVisible ? ( { this.setState({ isVisible: false }); }} onConfirm={this.confirmDelete} - cancelButtonText={localizedContent.cancelButtonOnDeleteCancelText} - confirmButtonText={localizedContent.confirmButtonOnDeleteComfirmText} + cancelButtonText={LocalizedContent.cancelButtonOnDeleteCancelText} + confirmButtonText={LocalizedContent.confirmButtonOnDeleteComfirmText} defaultFocusedButton="confirm" > -

{localizedContent.deleteCredentialDescribeMsg}

-

{localizedContent.deleteCredentialConfirmMsg}

-

{localizedContent.deleteCredentialWarnMsg}

+

{LocalizedContent.deleteCredentialDescribeMsg}

+

{LocalizedContent.deleteCredentialConfirmMsg}

+

{LocalizedContent.deleteCredentialWarnMsg}

) : null} @@ -148,7 +151,7 @@ export class EditCredentialComponent extends React.Component< renderContent() { const options = [ { - value: Credential.CredentialMaterialsType.UsernamePasswordType, + value: CredentialMaterialsType.UsernamePasswordType, text: 'Username and Password Credential', }, ]; diff --git a/src/plugins/credential_management/public/components/edit_credential_wizard/components/index.tsx b/src/plugins/credential_management/public/components/edit_credential_wizard/components/index.tsx new file mode 100644 index 000000000000..2b25cc821865 --- /dev/null +++ b/src/plugins/credential_management/public/components/edit_credential_wizard/components/index.tsx @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { EditCredential } from './edit_credential'; diff --git a/src/plugins/credential_management/public/components/edit_credential_wizard/edit_credential_wizard.tsx b/src/plugins/credential_management/public/components/edit_credential_wizard/edit_credential_wizard.tsx new file mode 100644 index 000000000000..8975c5542134 --- /dev/null +++ b/src/plugins/credential_management/public/components/edit_credential_wizard/edit_credential_wizard.tsx @@ -0,0 +1,88 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState } from 'react'; +import { useEffectOnce } from 'react-use'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { FormattedMessage } from '@osd/i18n/react'; +import { EuiGlobalToastListToast } from '@elastic/eui'; +import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; +import { + CredentialSavedObjectAttributes, + CREDENTIAL_SAVED_OBJECT_TYPE, +} from '../../../../data_source/public'; +import { CredentialManagementContext, ToastMessageItem } from '../../types'; +import { getEditBreadcrumbs } from '../breadcrumbs'; +import { EditCredentialItem } from '../types'; +import { EditCredential } from './components'; + +const EditCredentialWizard: React.FunctionComponent> = ({ + ...props +}) => { + /* Initialization */ + const { savedObjects, setBreadcrumbs } = useOpenSearchDashboards< + CredentialManagementContext + >().services; + + /* State Variables */ + const [credential, setCredential] = useState(); + const [toasts, setToasts] = useState([]); + + /* Fetch credential by id*/ + useEffectOnce(() => { + (async function () { + try { + const savedObject = await savedObjects.client.get( + CREDENTIAL_SAVED_OBJECT_TYPE, + props.match.params.id + ); + + const { title, description, credentialMaterials } = savedObject.attributes; + const { username } = credentialMaterials.credentialMaterialsContent; + const object = { + id: savedObject.id, + title, + description, + username, + password: '', + }; + setCredential(object); + setBreadcrumbs(getEditBreadcrumbs(object)); + } catch (e) { + handleDisplayToastMessage({ + id: 'dataSourcesManagement.editDataSource.editDataSourceFailMsg', + defaultMessage: 'Unable to find the Data Source. Please try it again.', + color: 'warning', + iconType: 'alert', + }); + + props.history.push(''); + } + })(); + }); + + const handleDisplayToastMessage = ({ id, defaultMessage, color, iconType }: ToastMessageItem) => { + if (id && defaultMessage && color && iconType) { + const failureMsg = ; + setToasts([ + ...toasts, + { + title: failureMsg, + id: failureMsg.props.id, + color, + iconType, + }, + ]); + } + }; + + if (credential) { + return ; + } else { + return

Credential not found!

; + } +}; + +export const EditCredentialPageWithRouter = withRouter(EditCredentialWizard); diff --git a/src/plugins/credential_management/public/components/edit_credential/index.tsx b/src/plugins/credential_management/public/components/edit_credential_wizard/index.tsx similarity index 94% rename from src/plugins/credential_management/public/components/edit_credential/index.tsx rename to src/plugins/credential_management/public/components/edit_credential_wizard/index.tsx index d1126c3073c4..51322900d69b 100644 --- a/src/plugins/credential_management/public/components/edit_credential/index.tsx +++ b/src/plugins/credential_management/public/components/edit_credential_wizard/index.tsx @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export { EditCredentialPageWithRouter } from './edit_credential_page'; +export { EditCredentialPageWithRouter } from './edit_credential_wizard'; diff --git a/src/plugins/credential_management/public/components/index.tsx b/src/plugins/credential_management/public/components/index.tsx index 9f6dff6f1f6c..d0c88a289a9e 100644 --- a/src/plugins/credential_management/public/components/index.tsx +++ b/src/plugins/credential_management/public/components/index.tsx @@ -5,4 +5,4 @@ export { CredentialsTableWithRouter } from './credential_table'; export { CreateCredentialWizardWithRouter } from './create_credential_wizard'; -export { EditCredentialPageWithRouter } from './edit_credential'; +export { EditCredentialPageWithRouter } from './edit_credential_wizard'; diff --git a/src/plugins/credential_management/public/components/types.ts b/src/plugins/credential_management/public/components/types.ts index 79e7cb388b0c..34b2a0335b8e 100644 --- a/src/plugins/credential_management/public/components/types.ts +++ b/src/plugins/credential_management/public/components/types.ts @@ -3,15 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -export interface CredentialsTableItem { - id: string; +export interface CredentialSpec { title: string; - credentialMaterialsType: string; + description?: string; +} + +export interface CredentialsTableItem extends CredentialSpec { + id: string; sort: string; } -export interface CredentialEditPageItem { +export interface CreateCredentialItem extends CredentialSpec { + username: string; + password: string; +} + +export interface EditCredentialItem extends CredentialSpec { id: string; - title: string; - credentialMaterialsType: string; + username: string; } diff --git a/src/plugins/credential_management/public/components/utils.ts b/src/plugins/credential_management/public/components/utils.ts index 69ea22c77d97..1efcd29af942 100644 --- a/src/plugins/credential_management/public/components/utils.ts +++ b/src/plugins/credential_management/public/components/utils.ts @@ -10,7 +10,7 @@ export async function getCredentials(savedObjectsClient: SavedObjectsClientContr return await (savedObjectsClient .find({ type: 'credential', - fields: ['id', 'title', 'credentialMaterials'], + fields: ['id', 'title', 'description', 'credentialMaterials'], perPage: 10000, }) .then((response) => @@ -18,11 +18,13 @@ export async function getCredentials(savedObjectsClient: SavedObjectsClientContr .map((source) => { const id = source.id; const title = source.get('title'); + const description = source.get('description'); const credentialMaterialsType = source.get('credentialMaterials') ?.credentialMaterialsType; return { id, title, + description, credentialMaterialsType, sort: `${title}`, }; diff --git a/src/plugins/credential_management/public/types.ts b/src/plugins/credential_management/public/types.ts index 5d74607244e7..976e8dc2d05b 100644 --- a/src/plugins/credential_management/public/types.ts +++ b/src/plugins/credential_management/public/types.ts @@ -31,6 +31,13 @@ export interface CredentialManagementContext { setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']; } +export interface ToastMessageItem { + id: string; + defaultMessage: string; + color: 'primary' | 'success' | 'warning' | 'danger'; + iconType: string; +} + export type CredentialManagmentContextValue = OpenSearchDashboardsReactContextValue< CredentialManagementContext >; diff --git a/src/plugins/data_source/common/credentials/index.ts b/src/plugins/data_source/common/credentials/index.ts index 70bfde226f9f..f8ecfe9f9262 100644 --- a/src/plugins/data_source/common/credentials/index.ts +++ b/src/plugins/data_source/common/credentials/index.ts @@ -3,4 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -export * as Credential from './types'; +export { + CredentialMaterialsType, + CredentialSavedObjectAttributes, + CredentialMaterials, + UsernamePasswordTypedContent, +} from './types'; diff --git a/src/plugins/data_source/common/index.ts b/src/plugins/data_source/common/index.ts index a2ae41868343..168de1a6e3e6 100644 --- a/src/plugins/data_source/common/index.ts +++ b/src/plugins/data_source/common/index.ts @@ -8,4 +8,9 @@ export const PLUGIN_NAME = 'data_source'; export const DATA_SOURCE_SAVED_OBJECT_TYPE = 'data-source'; export const CREDENTIAL_SAVED_OBJECT_TYPE = 'credential'; -export { Credential } from './credentials'; +export { + CredentialMaterialsType, + CredentialSavedObjectAttributes, + CredentialMaterials, + UsernamePasswordTypedContent, +} from './credentials'; diff --git a/src/plugins/data_source/public/index.ts b/src/plugins/data_source/public/index.ts index 1cf33f802870..05324bbc42af 100644 --- a/src/plugins/data_source/public/index.ts +++ b/src/plugins/data_source/public/index.ts @@ -13,4 +13,10 @@ export function plugin() { export { DataSourcePublicPluginSetup, DataSourcePublicPluginStart } from './types'; -export { Credential } from '../common'; +export { + CredentialMaterialsType, + CredentialSavedObjectAttributes, + CredentialMaterials, + UsernamePasswordTypedContent, + CREDENTIAL_SAVED_OBJECT_TYPE, +} from '../common'; diff --git a/src/plugins/data_source/server/saved_objects/credential_saved_objects_client_wrapper.ts b/src/plugins/data_source/server/saved_objects/credential_saved_objects_client_wrapper.ts index 0115c7012c6a..4e116ce61023 100644 --- a/src/plugins/data_source/server/saved_objects/credential_saved_objects_client_wrapper.ts +++ b/src/plugins/data_source/server/saved_objects/credential_saved_objects_client_wrapper.ts @@ -19,15 +19,13 @@ import { SavedObjectsErrorHelpers } from '../../../../core/server'; import { CryptographyClient } from '../cryptography'; -import { Credential } from '../../common'; +import { CredentialMaterialsType, CREDENTIAL_SAVED_OBJECT_TYPE } from '../../common'; /** * Describes the Credential Saved Objects Client Wrapper class, * which contains the factory used to create Saved Objects Client Wrapper instances */ export class CredentialSavedObjectsClientWrapper { - private type: string = 'credential'; - constructor(private cryptographyClient: CryptographyClient) {} /** @@ -43,7 +41,7 @@ export class CredentialSavedObjectsClientWrapper { attributes: T, options?: SavedObjectsCreateOptions ) => { - if (this.type !== type) { + if (CREDENTIAL_SAVED_OBJECT_TYPE !== type) { return await wrapperOptions.client.create(type, attributes, options); } @@ -60,7 +58,7 @@ export class CredentialSavedObjectsClientWrapper { objects.map(async (object) => { const { type, attributes } = object; - if (this.type !== type) { + if (CREDENTIAL_SAVED_OBJECT_TYPE !== type) { return object; } @@ -79,7 +77,7 @@ export class CredentialSavedObjectsClientWrapper { attributes: Partial, options: SavedObjectsUpdateOptions = {} ): Promise> => { - if (this.type !== type) { + if (CREDENTIAL_SAVED_OBJECT_TYPE !== type) { return await wrapperOptions.client.update(type, id, attributes, options); } @@ -98,7 +96,7 @@ export class CredentialSavedObjectsClientWrapper { objects.map(async (object) => { const { type, attributes } = object; - if (this.type !== type) { + if (CREDENTIAL_SAVED_OBJECT_TYPE !== type) { return object; } @@ -197,7 +195,7 @@ export class CredentialSavedObjectsClientWrapper { const { credentialMaterialsType, credentialMaterialsContent } = credentialMaterials; switch (credentialMaterialsType) { - case Credential.CredentialMaterialsType.UsernamePasswordType: + case CredentialMaterialsType.UsernamePasswordType: this.validateUsernamePasswordTypedContent(credentialMaterialsContent); return { ...attributes,