mirror of
https://github.com/harness/drone.git
synced 2025-05-10 22:21:22 +08:00
164 lines
5.9 KiB
TypeScript
164 lines
5.9 KiB
TypeScript
/*
|
|
* Copyright 2023 Harness, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { Container, PageBody, Text } from '@harnessio/uicore'
|
|
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
|
import cx from 'classnames'
|
|
import { useParams } from 'react-router-dom'
|
|
import { useGet } from 'restful-react'
|
|
import { Color, FontVariation } from '@harnessio/design-system'
|
|
import { routes, type CODEProps } from 'RouteDefinitions'
|
|
import type { TypesExecution } from 'services/code'
|
|
import ExecutionStageList from 'components/ExecutionStageList/ExecutionStageList'
|
|
import Console from 'components/Console/Console'
|
|
import { getErrorMessage, voidFn } from 'utils/Utils'
|
|
import { useStrings } from 'framework/strings'
|
|
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
|
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
|
|
import { Split } from 'components/Split/Split'
|
|
import { ExecutionPageHeader } from 'components/ExecutionPageHeader/ExecutionPageHeader'
|
|
import useSpaceSSE from 'hooks/useSpaceSSE'
|
|
import { ExecutionState } from 'components/ExecutionStatus/ExecutionStatus'
|
|
import { getStatus } from 'utils/ExecutionUtils'
|
|
import noExecutionImage from '../RepositoriesListing/no-repo.svg'
|
|
import css from './Execution.module.scss'
|
|
|
|
const Execution = () => {
|
|
const { pipeline, execution: executionNum } = useParams<CODEProps>()
|
|
const { getString } = useStrings()
|
|
|
|
const { repoMetadata, error, loading, refetch, space } = useGetRepositoryMetadata()
|
|
|
|
const {
|
|
data: execution,
|
|
error: executionError,
|
|
loading: executionLoading,
|
|
refetch: executionRefetch
|
|
} = useGet<TypesExecution>({
|
|
path: `/api/v1/repos/${repoMetadata?.path}/+/pipelines/${pipeline}/executions/${executionNum}`,
|
|
lazy: !repoMetadata
|
|
})
|
|
|
|
//TODO remove null type here?
|
|
const [selectedStage, setSelectedStage] = useState<number | null>(1)
|
|
//TODO - do not want to show load between refetchs - remove if/when we move to event stream method
|
|
const [isInitialLoad, setIsInitialLoad] = useState(true)
|
|
|
|
useEffect(() => {
|
|
if (execution) {
|
|
setIsInitialLoad(false)
|
|
}
|
|
}, [execution])
|
|
|
|
const onEvent = useCallback(
|
|
data => {
|
|
if (
|
|
data?.repo_id === execution?.repo_id &&
|
|
data?.pipeline_id === execution?.pipeline_id &&
|
|
data?.number === execution?.number
|
|
) {
|
|
//TODO - revisit full refresh - can I use the message to update the execution?
|
|
executionRefetch()
|
|
}
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[execution?.number, execution?.pipeline_id, execution?.repo_id]
|
|
)
|
|
|
|
const shouldRun = useMemo(() => {
|
|
return [ExecutionState.RUNNING, ExecutionState.PENDING].includes(getStatus(execution?.status))
|
|
}, [execution?.status])
|
|
|
|
const events = useMemo(
|
|
() => ['execution_updated', 'execution_completed', 'execution_canceled', 'execution_running'],
|
|
[]
|
|
)
|
|
|
|
useSpaceSSE({
|
|
space,
|
|
events,
|
|
onEvent,
|
|
shouldRun
|
|
})
|
|
|
|
return (
|
|
<Container className={css.main}>
|
|
<ExecutionPageHeader
|
|
repoMetadata={repoMetadata}
|
|
title={pipeline as string}
|
|
dataTooltipId="repositoryExecution"
|
|
pipeline={pipeline as string}
|
|
execution={executionNum as string}
|
|
extraBreadcrumbLinks={
|
|
repoMetadata && [
|
|
{
|
|
label: getString('pageTitle.pipelines'),
|
|
url: routes.toCODEPipelines({ repoPath: repoMetadata.path as string })
|
|
},
|
|
{
|
|
label: getString('pageTitle.executions'),
|
|
url: routes.toCODEExecutions({ repoPath: repoMetadata.path as string, pipeline: pipeline as string })
|
|
}
|
|
]
|
|
}
|
|
executionInfo={{
|
|
message: (execution?.message || execution?.title) as string,
|
|
authorName: execution?.author_name as string,
|
|
authorEmail: execution?.author_email as string,
|
|
source: execution?.source as string,
|
|
hash: execution?.after as string,
|
|
status: execution?.status as string,
|
|
started: execution?.started as number,
|
|
finished: execution?.finished as number
|
|
}}
|
|
/>
|
|
<PageBody
|
|
className={cx(css.pageBody, { [css.withError]: !!error })}
|
|
error={error || executionError ? getErrorMessage(error || executionError) : null}
|
|
retryOnError={voidFn(refetch)}
|
|
noData={{
|
|
when: () => !execution && !loading && !executionLoading,
|
|
image: noExecutionImage,
|
|
message: getString('executions.noData')
|
|
// button: NewExecutionButton
|
|
}}>
|
|
<LoadingSpinner visible={loading || isInitialLoad} withBorder={!!execution && isInitialLoad} />
|
|
{execution?.error && (
|
|
<Container className={css.error}>
|
|
<Text font={{ variation: FontVariation.BODY }} color={Color.WHITE}>
|
|
{execution?.error}
|
|
</Text>
|
|
</Container>
|
|
)}
|
|
{execution && !execution?.error && (
|
|
<Split split="vertical" size={300} minSize={200} maxSize={400}>
|
|
<ExecutionStageList
|
|
stages={execution?.stages || []}
|
|
setSelectedStage={setSelectedStage}
|
|
selectedStage={selectedStage}
|
|
/>
|
|
{selectedStage && (
|
|
<Console stage={execution?.stages?.[selectedStage - 1]} repoPath={repoMetadata?.path as string} />
|
|
)}
|
|
</Split>
|
|
)}
|
|
</PageBody>
|
|
</Container>
|
|
)
|
|
}
|
|
|
|
export default Execution
|