mirror of
https://github.com/harness/drone.git
synced 2025-05-12 15:10:09 +08:00
Merge branch 'add-delete-secret-modal' of _OKE5H2PQKOUfzFFDuD4FA/default/CODE/gitness (#444)
This commit is contained in:
commit
0c7f62d444
@ -21,7 +21,7 @@ import { useStrings } from 'framework/strings'
|
|||||||
import type { OpenapiCreateSecretRequest, TypesSecret } from 'services/code'
|
import type { OpenapiCreateSecretRequest, TypesSecret } from 'services/code'
|
||||||
import { getErrorMessage } from 'utils/Utils'
|
import { getErrorMessage } from 'utils/Utils'
|
||||||
|
|
||||||
interface SecretFormData {
|
export interface SecretFormData {
|
||||||
value: string
|
value: string
|
||||||
description: string
|
description: string
|
||||||
name: string
|
name: string
|
||||||
@ -82,10 +82,7 @@ export const NewSecretModalButton: React.FC<NewSecretModalButtonProps> = ({
|
|||||||
onClose={hideModal}
|
onClose={hideModal}
|
||||||
title={''}
|
title={''}
|
||||||
style={{ width: 700, maxHeight: '95vh', overflow: 'auto' }}>
|
style={{ width: 700, maxHeight: '95vh', overflow: 'auto' }}>
|
||||||
<Layout.Vertical
|
<Layout.Vertical padding={{ left: 'xxlarge' }} style={{ height: '100%' }} data-testid="add-secret-modal">
|
||||||
padding={{ left: 'xxlarge' }}
|
|
||||||
style={{ height: '100%' }}
|
|
||||||
data-testid="add-target-to-flag-modal">
|
|
||||||
<Heading level={3} font={{ variation: FontVariation.H3 }} margin={{ bottom: 'xlarge' }}>
|
<Heading level={3} font={{ variation: FontVariation.H3 }} margin={{ bottom: 'xlarge' }}>
|
||||||
{modalTitle}
|
{modalTitle}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
156
web/src/components/UpdateSecretModal/UpdateSecretModal.tsx
Normal file
156
web/src/components/UpdateSecretModal/UpdateSecretModal.tsx
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import React, { useRef, useState } from 'react'
|
||||||
|
import * as yup from 'yup'
|
||||||
|
import { useMutate } from 'restful-react'
|
||||||
|
import { FontVariation, Intent } from '@harnessio/design-system'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
Layout,
|
||||||
|
Heading,
|
||||||
|
Container,
|
||||||
|
Formik,
|
||||||
|
FormikForm,
|
||||||
|
FormInput,
|
||||||
|
FlexExpander,
|
||||||
|
useToaster,
|
||||||
|
StringSubstitute
|
||||||
|
} from '@harnessio/uicore'
|
||||||
|
import { Icon } from '@harnessio/icons'
|
||||||
|
import { useStrings } from 'framework/strings'
|
||||||
|
import { useModalHook } from 'hooks/useModalHook'
|
||||||
|
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||||
|
import type { OpenapiUpdateSecretRequest, TypesSecret } from 'services/code'
|
||||||
|
import type { SecretFormData } from 'components/NewSecretModalButton/NewSecretModalButton'
|
||||||
|
import { getErrorMessage } from 'utils/Utils'
|
||||||
|
|
||||||
|
const useUpdateSecretModal = () => {
|
||||||
|
const { getString } = useStrings()
|
||||||
|
const space = useGetSpaceParam()
|
||||||
|
const { showError, showSuccess } = useToaster()
|
||||||
|
const [secret, setSecret] = useState<TypesSecret>()
|
||||||
|
const postUpdate = useRef<Function>()
|
||||||
|
|
||||||
|
const { mutate: updateSecret, loading } = useMutate<TypesSecret>({
|
||||||
|
verb: 'PATCH',
|
||||||
|
path: `/api/v1/secrets/${space}/${secret?.uid}/+`
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSubmit = async (formData: SecretFormData) => {
|
||||||
|
try {
|
||||||
|
const payload: OpenapiUpdateSecretRequest = {
|
||||||
|
data: formData.value,
|
||||||
|
description: formData.description,
|
||||||
|
uid: formData.name
|
||||||
|
}
|
||||||
|
await updateSecret(payload)
|
||||||
|
hideModal()
|
||||||
|
showSuccess(
|
||||||
|
<StringSubstitute
|
||||||
|
str={getString('secrets.secretUpdated')}
|
||||||
|
vars={{
|
||||||
|
uid: formData.name
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
postUpdate.current?.()
|
||||||
|
} catch (exception) {
|
||||||
|
showError(getErrorMessage(exception), 0, getString('secrets.failedToUpdateSecret'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [openModal, hideModal] = useModalHook(() => {
|
||||||
|
const onClose = () => {
|
||||||
|
hideModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
isOpen
|
||||||
|
enforceFocus={false}
|
||||||
|
onClose={hideModal}
|
||||||
|
title={''}
|
||||||
|
style={{ width: 700, maxHeight: '95vh', overflow: 'auto' }}>
|
||||||
|
<Layout.Vertical padding={{ left: 'xxlarge' }} style={{ height: '100%' }} data-testid="add-secret-modal">
|
||||||
|
<Heading level={3} font={{ variation: FontVariation.H3 }} margin={{ bottom: 'xlarge' }}>
|
||||||
|
{getString('secrets.updateSecret')}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
|
<Container margin={{ right: 'xxlarge' }}>
|
||||||
|
<Formik
|
||||||
|
initialValues={{ name: secret?.uid || '', description: secret?.description || '', value: '' }}
|
||||||
|
formName="addSecret"
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={yup.object().shape({
|
||||||
|
name: yup.string().trim().required(),
|
||||||
|
value: yup.string().trim().required()
|
||||||
|
})}
|
||||||
|
validateOnChange
|
||||||
|
validateOnBlur
|
||||||
|
onSubmit={handleSubmit}>
|
||||||
|
<FormikForm>
|
||||||
|
<FormInput.Text
|
||||||
|
name="name"
|
||||||
|
label={getString('name')}
|
||||||
|
placeholder={getString('secrets.enterSecretName')}
|
||||||
|
tooltipProps={{
|
||||||
|
dataTooltipId: 'secretNameTextField'
|
||||||
|
}}
|
||||||
|
inputGroup={{ autoFocus: true }}
|
||||||
|
/>
|
||||||
|
<FormInput.Text
|
||||||
|
name="value"
|
||||||
|
label={getString('value')}
|
||||||
|
placeholder={getString('secrets.value')}
|
||||||
|
tooltipProps={{
|
||||||
|
dataTooltipId: 'secretDescriptionTextField'
|
||||||
|
}}
|
||||||
|
inputGroup={{ type: 'password' }}
|
||||||
|
/>
|
||||||
|
<FormInput.Text
|
||||||
|
name="description"
|
||||||
|
label={getString('description')}
|
||||||
|
placeholder={getString('enterDescription')}
|
||||||
|
tooltipProps={{
|
||||||
|
dataTooltipId: 'secretDescriptionTextField'
|
||||||
|
}}
|
||||||
|
isOptional
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Layout.Horizontal
|
||||||
|
spacing="small"
|
||||||
|
padding={{ right: 'xxlarge', top: 'xxxlarge', bottom: 'large' }}
|
||||||
|
style={{ alignItems: 'center' }}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={getString('secrets.updateSecret')}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<Button text={getString('cancel')} minimal onClick={onClose} />
|
||||||
|
<FlexExpander />
|
||||||
|
{loading && <Icon intent={Intent.PRIMARY} name="steps-spinner" size={16} />}
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</FormikForm>
|
||||||
|
</Formik>
|
||||||
|
</Container>
|
||||||
|
</Layout.Vertical>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}, [secret])
|
||||||
|
|
||||||
|
return {
|
||||||
|
openModal: ({
|
||||||
|
secretToUpdate,
|
||||||
|
openSecretUpdate
|
||||||
|
}: {
|
||||||
|
secretToUpdate: TypesSecret
|
||||||
|
openSecretUpdate: () => Promise<void>
|
||||||
|
}) => {
|
||||||
|
setSecret(secretToUpdate)
|
||||||
|
postUpdate.current = openSecretUpdate
|
||||||
|
openModal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useUpdateSecretModal
|
@ -496,10 +496,13 @@ export interface StringsMap {
|
|||||||
'secrets.enterSecretName': string
|
'secrets.enterSecretName': string
|
||||||
'secrets.failedToCreate': string
|
'secrets.failedToCreate': string
|
||||||
'secrets.failedToDeleteSecret': string
|
'secrets.failedToDeleteSecret': string
|
||||||
|
'secrets.failedToUpdateSecret': string
|
||||||
'secrets.name': string
|
'secrets.name': string
|
||||||
'secrets.newSecretButton': string
|
'secrets.newSecretButton': string
|
||||||
'secrets.noData': string
|
'secrets.noData': string
|
||||||
'secrets.secretDeleted': string
|
'secrets.secretDeleted': string
|
||||||
|
'secrets.secretUpdated': string
|
||||||
|
'secrets.updateSecret': string
|
||||||
'secrets.value': string
|
'secrets.value': string
|
||||||
selectBranchPlaceHolder: string
|
selectBranchPlaceHolder: string
|
||||||
selectRange: string
|
selectRange: string
|
||||||
|
@ -666,9 +666,12 @@ secrets:
|
|||||||
createSecret: Create Secret
|
createSecret: Create Secret
|
||||||
createSuccess: Secret created successfully
|
createSuccess: Secret created successfully
|
||||||
secretDeleted: Secret {uid} deleted.
|
secretDeleted: Secret {uid} deleted.
|
||||||
|
secretUpdated: Secret {uid} updated.
|
||||||
deleteSecretConfirm: Are you sure you want to delete secret <strong>{{uid}}</strong>? You can't undo this action.
|
deleteSecretConfirm: Are you sure you want to delete secret <strong>{{uid}}</strong>? You can't undo this action.
|
||||||
failedToDeleteSecret: Failed to delete Secret. Please try again.
|
failedToDeleteSecret: Failed to delete Secret. Please try again.
|
||||||
deleteSecret: Delete Secrets
|
failedToUpdateSecret: Failed to update Secret. Please try again.
|
||||||
|
deleteSecret: Delete secret
|
||||||
|
updateSecret: Update secret
|
||||||
userUpdateSuccess: 'User updated successfully'
|
userUpdateSuccess: 'User updated successfully'
|
||||||
viewFile: View File
|
viewFile: View File
|
||||||
searchResult: 'Search Result {count}'
|
searchResult: 'Search Result {count}'
|
||||||
|
@ -8,14 +8,13 @@ import {
|
|||||||
PageBody,
|
PageBody,
|
||||||
TableV2 as Table,
|
TableV2 as Table,
|
||||||
Text,
|
Text,
|
||||||
Utils,
|
Utils
|
||||||
useToaster
|
|
||||||
} from '@harnessio/uicore'
|
} from '@harnessio/uicore'
|
||||||
import { Color } from '@harnessio/design-system'
|
import { Color } from '@harnessio/design-system'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
import type { CellProps, Column } from 'react-table'
|
import type { CellProps, Column } from 'react-table'
|
||||||
import { useHistory, useParams } from 'react-router-dom'
|
import { useHistory, useParams } from 'react-router-dom'
|
||||||
import { useGet, useMutate } from 'restful-react'
|
import { useGet } from 'restful-react'
|
||||||
import { Timer, Calendar } from 'iconoir-react'
|
import { Timer, Calendar } from 'iconoir-react'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
||||||
@ -33,6 +32,7 @@ import { ExecutionStatus } from 'components/ExecutionStatus/ExecutionStatus'
|
|||||||
import { getStatus } from 'utils/PipelineUtils'
|
import { getStatus } from 'utils/PipelineUtils'
|
||||||
import useSpaceSSE from 'hooks/useSpaceSSE'
|
import useSpaceSSE from 'hooks/useSpaceSSE'
|
||||||
import { ExecutionText, ExecutionTrigger } from 'components/ExecutionText/ExecutionText'
|
import { ExecutionText, ExecutionTrigger } from 'components/ExecutionText/ExecutionText'
|
||||||
|
import useRunPipelineModal from 'components/RunPipelineModal/RunPipelineModal'
|
||||||
import noExecutionImage from '../RepositoriesListing/no-repo.svg'
|
import noExecutionImage from '../RepositoriesListing/no-repo.svg'
|
||||||
import css from './ExecutionList.module.scss'
|
import css from './ExecutionList.module.scss'
|
||||||
|
|
||||||
@ -44,7 +44,6 @@ const ExecutionList = () => {
|
|||||||
const pageBrowser = useQueryParams<PageBrowserProps>()
|
const pageBrowser = useQueryParams<PageBrowserProps>()
|
||||||
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
|
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
|
||||||
const [page, setPage] = usePageIndex(pageInit)
|
const [page, setPage] = usePageIndex(pageInit)
|
||||||
const { showError, showSuccess } = useToaster()
|
|
||||||
|
|
||||||
const { repoMetadata, error, loading, refetch, space } = useGetRepositoryMetadata()
|
const { repoMetadata, error, loading, refetch, space } = useGetRepositoryMetadata()
|
||||||
|
|
||||||
@ -84,19 +83,11 @@ const ExecutionList = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutate, loading: mutateLoading } = useMutate<TypesExecution>({
|
const { openModal: openRunPipelineModal } = useRunPipelineModal()
|
||||||
verb: 'POST',
|
|
||||||
path: `/api/v1/repos/${repoMetadata?.path}/+/pipelines/${pipeline}/executions`
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
try {
|
if (repoMetadata && pipeline) {
|
||||||
//TODO - this should NOT be hardcoded to master branch - need a modal to insert branch - but useful for testing until then
|
openRunPipelineModal({ repoMetadata, pipeline })
|
||||||
await mutate({ branch: 'master' })
|
|
||||||
showSuccess('Build started')
|
|
||||||
executionsRefetch()
|
|
||||||
} catch {
|
|
||||||
showError('Failed to start build')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +96,6 @@ const ExecutionList = () => {
|
|||||||
text={getString('executions.newExecutionButton')}
|
text={getString('executions.newExecutionButton')}
|
||||||
variation={ButtonVariation.PRIMARY}
|
variation={ButtonVariation.PRIMARY}
|
||||||
icon="play-outline"
|
icon="play-outline"
|
||||||
disabled={mutateLoading}
|
|
||||||
onClick={handleClick}></Button>
|
onClick={handleClick}></Button>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import { ResourceListingPagination } from 'components/ResourceListingPagination/
|
|||||||
import { NewSecretModalButton } from 'components/NewSecretModalButton/NewSecretModalButton'
|
import { NewSecretModalButton } from 'components/NewSecretModalButton/NewSecretModalButton'
|
||||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||||
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
||||||
|
import useUpdateSecretModal from 'components/UpdateSecretModal/UpdateSecretModal'
|
||||||
import noSecretsImage from '../RepositoriesListing/no-repo.svg'
|
import noSecretsImage from '../RepositoriesListing/no-repo.svg'
|
||||||
import css from './SecretList.module.scss'
|
import css from './SecretList.module.scss'
|
||||||
|
|
||||||
@ -61,6 +62,8 @@ const SecretList = () => {
|
|||||||
onSuccess={() => refetch()}></NewSecretModalButton>
|
onSuccess={() => refetch()}></NewSecretModalButton>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { openModal: openUpdateSecretModal } = useUpdateSecretModal()
|
||||||
|
|
||||||
const columns: Column<TypesSecret>[] = useMemo(
|
const columns: Column<TypesSecret>[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@ -113,6 +116,11 @@ const SecretList = () => {
|
|||||||
isDark
|
isDark
|
||||||
width="100px"
|
width="100px"
|
||||||
items={[
|
items={[
|
||||||
|
{
|
||||||
|
text: getString('edit'),
|
||||||
|
isDanger: true,
|
||||||
|
onClick: () => openUpdateSecretModal({ secretToUpdate: row.original, openSecretUpdate: refetch })
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: getString('delete'),
|
text: getString('delete'),
|
||||||
isDanger: true,
|
isDanger: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user