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' } { category: PluginCategory.Drone, name: 'Drone', description: 'Run Drone plugins', icon: 'ci-infra' }
] ]
const pluginSpecMock = { const dronePluginSpecMock = {
inputs: { inputs: {
channel: { channel: {
type: 'string' type: 'string'
@ -61,6 +61,14 @@ const pluginSpecMock = {
] ]
} }
const runStepSpec = {
inputs: {
script: {
type: 'string'
}
}
}
export interface PluginsPanelInterface { export interface PluginsPanelInterface {
onPluginAddUpdate?: (isUpdate: boolean, pluginFormData: Record<string, any>) => void onPluginAddUpdate?: (isUpdate: boolean, pluginFormData: Record<string, any>) => void
} }
@ -101,6 +109,9 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
setCategory(category) setCategory(category)
if (category === PluginCategory.Drone) { if (category === PluginCategory.Drone) {
setPanelView(PluginPanelView.Listing) setPanelView(PluginPanelView.Listing)
} else if (category === PluginCategory.Harness) {
setPlugin({ uid: getString('run') })
setPanelView(PluginPanelView.Configuration)
} }
}} }}
key={category} key={category}
@ -187,15 +198,8 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
} }
const renderPluginConfigForm = useCallback((): JSX.Element => { const renderPluginConfigForm = useCallback((): JSX.Element => {
const { uid, spec } = plugin || {} // TODO obtain plugin input spec by parsing YAML
if (spec) { const inputs = get(category === PluginCategory.Drone ? dronePluginSpecMock : runStepSpec, 'inputs', {})
// let specAsObj = {}
// try {
// specAsObj = parse(spec)
// } catch (e) {
// // ignore error
// }
const inputs = get(pluginSpecMock, 'inputs', {})
return ( return (
<Layout.Vertical <Layout.Vertical
spacing="medium" spacing="medium"
@ -207,13 +211,21 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
size={18} size={18}
onClick={() => { onClick={() => {
setPlugin(undefined) setPlugin(undefined)
if (category === PluginCategory.Drone) {
setPanelView(PluginPanelView.Listing) setPanelView(PluginPanelView.Listing)
} else if (category === PluginCategory.Harness) {
setPanelView(PluginPanelView.Category)
}
}} }}
className={css.arrow} className={css.arrow}
/> />
{plugin?.uid ? (
<Text font={{ variation: FontVariation.H5 }}> <Text font={{ variation: FontVariation.H5 }}>
{getString('addLabel')} {uid} {getString('addLabel')} {plugin.uid} {getString('plugins.stepLabel')}
</Text> </Text>
) : (
<></>
)}
</Layout.Horizontal> </Layout.Horizontal>
<Container className={css.form}> <Container className={css.form}>
<Formik <Formik
@ -236,9 +248,7 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
</Container> </Container>
</Layout.Vertical> </Layout.Vertical>
) )
} }, [plugin, category])
return <></>
}, [plugin])
const renderPluginsPanel = useCallback((): JSX.Element => { const renderPluginsPanel = useCallback((): JSX.Element => {
switch (panelView) { switch (panelView) {
@ -251,7 +261,7 @@ export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.
default: default:
return <></> return <></>
} }
}, [loading, plugins, panelView]) }, [loading, plugins, panelView, category])
return ( return (
<Container className={css.main}> <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.enterPipelineName': string
'pipelines.enterYAMLPath': string 'pipelines.enterYAMLPath': string
'pipelines.failedToCreatePipeline': string 'pipelines.failedToCreatePipeline': string
'pipelines.failedToRunPipeline': string
'pipelines.name': string 'pipelines.name': string
'pipelines.newPipelineButton': string 'pipelines.newPipelineButton': string
'pipelines.noData': string 'pipelines.noData': string
@ -330,6 +331,7 @@ export interface StringsMap {
'pipelines.saveAndRun': string 'pipelines.saveAndRun': string
'pipelines.yamlPath': string 'pipelines.yamlPath': string
'plugins.addAPlugin': string 'plugins.addAPlugin': string
'plugins.stepLabel': string
'plugins.title': string 'plugins.title': string
'pr.ableToMerge': string 'pr.ableToMerge': string
'pr.addDescription': string 'pr.addDescription': string

View File

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

View File

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

View File

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