run pipeline integration

This commit is contained in:
Vardan Bansal 2023-09-07 03:27:03 -07:00
parent 7735f2b7a7
commit b0a4946d80
6 changed files with 176 additions and 68 deletions

View File

@ -39,7 +39,7 @@ const PluginCategories: PluginInterface[] = [
{ category: PluginCategory.Drone, name: 'Drone', description: 'Run Drone plugins', icon: 'ci-infra' }
]
const pluginSpecMock = {
const dronePluginSpecMock = {
inputs: {
channel: {
type: 'string'
@ -61,6 +61,14 @@ const pluginSpecMock = {
]
}
const runStepSpec = {
inputs: {
script: {
type: 'string'
}
}
}
export interface PluginsPanelInterface {
onPluginAddUpdate?: (isUpdate: boolean, pluginFormData: Record<string, any>) => void
}
@ -101,6 +109,9 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
setCategory(category)
if (category === PluginCategory.Drone) {
setPanelView(PluginPanelView.Listing)
} else if (category === PluginCategory.Harness) {
setPlugin({ uid: getString('run') })
setPanelView(PluginPanelView.Configuration)
}
}}
key={category}
@ -187,58 +198,57 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
}
const renderPluginConfigForm = useCallback((): JSX.Element => {
const { uid, spec } = plugin || {}
if (spec) {
// let specAsObj = {}
// try {
// specAsObj = parse(spec)
// } catch (e) {
// // ignore error
// }
const inputs = get(pluginSpecMock, 'inputs', {})
return (
<Layout.Vertical
spacing="medium"
margin={{ left: 'xxlarge', top: 'large', right: 'xxlarge', bottom: 'xxlarge' }}
height="95%">
<Layout.Horizontal spacing="small" flex={{ justifyContent: 'flex-start' }}>
<Icon
name="arrow-left"
size={18}
onClick={() => {
setPlugin(undefined)
// TODO obtain plugin input spec by parsing YAML
const inputs = get(category === PluginCategory.Drone ? dronePluginSpecMock : runStepSpec, 'inputs', {})
return (
<Layout.Vertical
spacing="medium"
margin={{ left: 'xxlarge', top: 'large', right: 'xxlarge', bottom: 'xxlarge' }}
height="95%">
<Layout.Horizontal spacing="small" flex={{ justifyContent: 'flex-start' }}>
<Icon
name="arrow-left"
size={18}
onClick={() => {
setPlugin(undefined)
if (category === PluginCategory.Drone) {
setPanelView(PluginPanelView.Listing)
}}
className={css.arrow}
/>
} else if (category === PluginCategory.Harness) {
setPanelView(PluginPanelView.Category)
}
}}
className={css.arrow}
/>
{plugin?.uid ? (
<Text font={{ variation: FontVariation.H5 }}>
{getString('addLabel')} {uid}
{getString('addLabel')} {plugin.uid} {getString('plugins.stepLabel')}
</Text>
</Layout.Horizontal>
<Container className={css.form}>
<Formik
initialValues={{}}
onSubmit={formData => {
onPluginAddUpdate?.(false, formData)
}}>
<FormikForm>
<Layout.Vertical flex={{ alignItems: 'flex-start' }} height="100%">
<Layout.Vertical width="100%">
{Object.keys(inputs).map((field: string) => {
const fieldType = get(inputs, `${field}.type`, '') as 'string'
return renderPluginFormField({ name: field, type: fieldType })
})}
</Layout.Vertical>
<Button variation={ButtonVariation.PRIMARY} text={getString('addLabel')} type="submit" />
) : (
<></>
)}
</Layout.Horizontal>
<Container className={css.form}>
<Formik
initialValues={{}}
onSubmit={formData => {
onPluginAddUpdate?.(false, formData)
}}>
<FormikForm>
<Layout.Vertical flex={{ alignItems: 'flex-start' }} height="100%">
<Layout.Vertical width="100%">
{Object.keys(inputs).map((field: string) => {
const fieldType = get(inputs, `${field}.type`, '') as 'string'
return renderPluginFormField({ name: field, type: fieldType })
})}
</Layout.Vertical>
</FormikForm>
</Formik>
</Container>
</Layout.Vertical>
)
}
return <></>
}, [plugin])
<Button variation={ButtonVariation.PRIMARY} text={getString('addLabel')} type="submit" />
</Layout.Vertical>
</FormikForm>
</Formik>
</Container>
</Layout.Vertical>
)
}, [plugin, category])
const renderPluginsPanel = useCallback((): JSX.Element => {
switch (panelView) {
@ -251,7 +261,7 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
default:
return <></>
}
}, [loading, plugins, panelView])
}, [loading, plugins, panelView, category])
return (
<Container className={css.main}>

View File

@ -0,0 +1,103 @@
import React, { useMemo, useState } from 'react'
import * as yup from 'yup'
import { FontVariation } from '@harnessio/design-system'
import {
Button,
ButtonVariation,
Dialog,
FormInput,
Formik,
FormikForm,
Layout,
Text,
useToaster
} from '@harnessio/uicore'
import { useStrings } from 'framework/strings'
import { useModalHook } from 'hooks/useModalHook'
import type { OpenapiCreateExecutionRequest, TypesExecution, TypesRepository } from 'services/code'
import { useMutate } from 'restful-react'
import { getErrorMessage } from 'utils/Utils'
import { useHistory } from 'react-router'
import { useAppContext } from 'AppContext'
interface FormData {
branch: string
}
const useRunPipelineModal = () => {
const { routes } = useAppContext()
const { getString } = useStrings()
const { showError } = useToaster()
const history = useHistory()
const [repo, setRepo] = useState<TypesRepository>()
const [pipeline, setPipeline] = useState<string>('')
const repoPath = useMemo(() => repo?.path || '', [repo])
const { mutate: startExecution } = useMutate<TypesExecution>({
verb: 'POST',
path: `/api/v1/repos/${repoPath}/+/pipelines/${pipeline}/executions`
})
const runPipeline = (_formData: FormData): void => {
try {
const payload: OpenapiCreateExecutionRequest = {
status: ''
}
startExecution(payload, { pathParams: { path: `/api/v1/repos/${repoPath}/+/pipelines/${pipeline}/executions` } })
.then(() => {
history.push(routes.toCODEExecutions({ repoPath, pipeline }))
hideModal()
})
.catch(error => {
showError(getErrorMessage(error), 0, 'pipelines.failedToRunPipeline')
})
} catch (exception) {
showError(getErrorMessage(exception), 0, 'pipelines.failedToRunPipeline')
}
}
const [openModal, hideModal] = useModalHook(() => {
const onClose = () => {
hideModal()
}
return (
<Dialog isOpen enforceFocus={false} onClose={onClose} title={getString('pipelines.run')}>
<Formik
formName="run-pipeline-form"
initialValues={{ branch: repo?.default_branch || '' }}
validationSchema={yup.object().shape({
branch: yup
.string()
.trim()
.required(`${getString('branch')} ${getString('isRequired')}`)
})}
onSubmit={runPipeline}
enableReinitialize>
<FormikForm>
<Layout.Vertical spacing="medium">
<FormInput.Text
name="branch"
label={<Text font={{ variation: FontVariation.FORM_LABEL }}>{getString('branch')}</Text>}
/>
<Layout.Horizontal spacing="medium">
<Button variation={ButtonVariation.PRIMARY} type="submit" text={getString('pipelines.run')} />
<Button variation={ButtonVariation.SECONDARY} text={getString('cancel')} onClick={onClose} />
</Layout.Horizontal>
</Layout.Vertical>
</FormikForm>
</Formik>
</Dialog>
)
}, [repo?.default_branch, pipeline])
return {
openModal: ({ repoMetadata, pipeline }: { repoMetadata: TypesRepository; pipeline: string }) => {
setRepo(repoMetadata)
setPipeline(pipeline)
openModal()
},
hideModal
}
}
export default useRunPipelineModal

View File

@ -323,6 +323,7 @@ export interface StringsMap {
'pipelines.enterPipelineName': string
'pipelines.enterYAMLPath': string
'pipelines.failedToCreatePipeline': string
'pipelines.failedToRunPipeline': string
'pipelines.name': string
'pipelines.newPipelineButton': string
'pipelines.noData': string
@ -330,6 +331,7 @@ export interface StringsMap {
'pipelines.saveAndRun': string
'pipelines.yamlPath': string
'plugins.addAPlugin': string
'plugins.stepLabel': string
'plugins.title': string
'pr.ableToMerge': string
'pr.addDescription': string

View File

@ -636,6 +636,7 @@ pipelines:
saveAndRun: Save and Run
editPipeline: Edit pipeline {{pipeline}}
run: Run pipeline
failedToRunPipeline: Failed to run pipeline
executions:
noData: There are no executions :(
newExecutionButton: Run Pipeline
@ -657,3 +658,4 @@ run: Run
plugins:
title: Plugins
addAPlugin: Add a {{category}} plugin
stepLabel: step

View File

@ -1,7 +1,6 @@
import React, { useMemo, useState } from 'react'
import { useMutate } from 'restful-react'
import { Link, useParams } from 'react-router-dom'
import { Drawer } from '@blueprintjs/core'
import { Container, PageHeader, PageBody, Button, Layout, ButtonVariation, Text, useToaster } from '@harnessio/uicore'
import { Icon } from '@harnessio/icons'
import { Color } from '@harnessio/design-system'
@ -10,6 +9,7 @@ import { useStrings } from 'framework/strings'
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
import MonacoSourceCodeEditor from 'components/SourceCodeEditor/MonacoSourceCodeEditor'
import { PluginsPanel } from 'components/PluginsPanel/PluginsPanel'
import useRunPipelineModal from 'components/RunPipelineModal/RunPipelineModal'
import { useAppContext } from 'AppContext'
import type { CODEProps } from 'RouteDefinitions'
import { getErrorMessage } from 'utils/Utils'
@ -24,7 +24,7 @@ const NewPipeline = (): JSX.Element => {
const { repoMetadata } = useGetRepositoryMetadata()
const { showError } = useToaster()
const [pipelineAsYAML, setPipelineAsYaml] = useState<string>('')
const [showDrawer, setShowDrawer] = useState<boolean>(false)
const { openModal: openRunPipelineModal } = useRunPipelineModal()
const repoPath = useMemo(() => repoMetadata?.path || '', [repoMetadata])
const { mutate, loading } = useMutate<RepoCommitFilesResponse>({
@ -36,14 +36,18 @@ const NewPipeline = (): JSX.Element => {
try {
const data: OpenapiCommitFilesRequest = {
actions: [{ action: 'CREATE', path: `sample_${new Date().getTime()}.txt`, payload: pipelineAsYAML }],
branch: 'main',
branch: repoMetadata?.default_branch,
new_branch: '',
title: `Create pipeline ${pipeline}`,
message: ''
}
mutate(data)
.then(() => setShowDrawer(true))
.then(() => {
if (repoMetadata && pipeline) {
openRunPipelineModal({ repoMetadata, pipeline })
}
})
.catch(error => {
showError(getErrorMessage(error), 0, 'pipelines.failedToSavePipeline')
})
@ -54,20 +58,6 @@ const NewPipeline = (): JSX.Element => {
return (
<>
{showDrawer && (
<Drawer isOpen={showDrawer} title={getString('pipelines.run')} onClose={() => setShowDrawer(false)}>
<Layout.Vertical padding="xlarge" flex={{ justifyContent: 'flex-end' }} className={css.drawer}>
<Layout.Horizontal spacing="medium" flex={{ justifyContent: 'flex-start' }} width="100%">
<Button variation={ButtonVariation.PRIMARY} text={getString('run')} />
<Button
variation={ButtonVariation.SECONDARY}
text={getString('cancel')}
onClick={() => setShowDrawer(false)}
/>
</Layout.Horizontal>
</Layout.Vertical>
</Drawer>
)}
<Container className={css.main}>
<PageHeader
title={getString('pipelines.editPipeline', { pipeline })}

View File

@ -8,6 +8,7 @@ import type { OpenapiCreatePipelineRequest, TypesPipeline, TypesRepository } fro
import { useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import { getErrorMessage } from 'utils/Utils'
interface FormData {
name: string
branch: string