Skip to content

Commit

Permalink
fix: pipeline related bugs (#907)
Browse files Browse the repository at this point in the history
  • Loading branch information
m8vago authored Feb 13, 2024
1 parent 8a1b2de commit 0d2e7a9
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 23 deletions.
4 changes: 4 additions & 0 deletions web/crux-ui/locales/en/pipelines.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,9 @@

"filters": {
"imageNameStartsWith": "Image name starts with"
},

"errors": {
"trigger": "Failed to start the pipeline. There might be a undeclared variables?"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const EditEventWatcherCard = (props: EditEventWatcherCardProps) => {
/>

<div className="mt-6">
<DyoLabel>{t('defaultInputs')}</DyoLabel>
<DyoLabel>{t('inputs')}</DyoLabel>

<DyoMessage className="text-xs mt-2" message={t('templateTips')} messageType="info" />

Expand Down
30 changes: 19 additions & 11 deletions web/crux-ui/src/components/pipelines/trigger-pipeline-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import DyoButton from '@app/elements/dyo-button'
import { DyoCard, DyoCardProps } from '@app/elements/dyo-card'
import DyoForm from '@app/elements/dyo-form'
import { DyoHeading } from '@app/elements/dyo-heading'
import DyoMessage from '@app/elements/dyo-message'
import { DyoConfirmationModal } from '@app/elements/dyo-modal'
import { fromApiError } from '@app/error-responses'
import { defaultApiErrorHandler } from '@app/errors'
import useConfirmation from '@app/hooks/use-confirmation'
import useTeamRoutes from '@app/hooks/use-team-routes'
import { PipelineDetails, PipelineRun } from '@app/models'
import { PipelineDetails, PipelineRun, TriggerPipeline } from '@app/models'
import { sendForm } from '@app/utils'
import clsx from 'clsx'
import useTranslation from 'next-translate/useTranslation'
import { QA_DIALOG_LABEL_TRIGGER_PIPELINE } from 'quality-assurance'
import { useState } from 'react'
import { OFFLINE_EDITOR_STATE } from '../editor/use-item-editor-state'
import KeyValueInput from '../shared/key-value-input'
import { QA_DIALOG_LABEL_TRIGGER_PIPELINE } from 'quality-assurance'

type TriggerPipelineCardProps = Omit<DyoCardProps, 'children'> & {
pipeline: PipelineDetails
Expand All @@ -30,10 +32,13 @@ const TriggerPipelineCard = (props: TriggerPipelineCardProps) => {

const [confirmConfig, confirm] = useConfirmation()
const [inputs, setInputs] = useState(pipeline.trigger.inputs)
const [error, setError] = useState<string>(null)

const handleApiError = defaultApiErrorHandler(t)

const onTrigger = async (): Promise<void> => {
setError(null)

const confirmed = await confirm({
title: t('common:areYouSure'),
description: t('areYouSureTriggerName', pipeline),
Expand All @@ -45,20 +50,21 @@ const TriggerPipelineCard = (props: TriggerPipelineCardProps) => {
return
}

const body: Record<string, string> =
inputs.length < 1
? null
: inputs.reduce((result, it) => {
const { key, value } = it

result[key] = value
return result
}, {})
const body: TriggerPipeline = {
inputs: inputs && inputs.length > 0 ? inputs : null,
}

const res = await sendForm('POST', routes.pipeline.api.runs(pipeline.id), body)

if (!res.ok) {
if (res.status === 400) {
const err = fromApiError(res.status, await res.json())
setError(err.description)
return
}

await handleApiError(res)
return
}

const run = (await res.json()) as PipelineRun
Expand All @@ -72,6 +78,8 @@ const TriggerPipelineCard = (props: TriggerPipelineCardProps) => {
{t('triggerPipeline')}
</DyoHeading>

<DyoMessage className="text-xs italic mb-4" message={error} messageType="info" />

<DyoForm className="flex flex-col gap-4">
<div className="flex flex-row self-end">
<DyoButton secondary outlined onClick={onClose}>
Expand Down
28 changes: 23 additions & 5 deletions web/crux-ui/src/components/pipelines/use-pipeline-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,29 @@ export const pipelineEdited =

export const pipelineTriggered =
(run: PipelineRun): RepatchAction<PipelineDetailsState> =>
state => ({
...state,
viewState: 'run-list',
runs: [run, ...state.runs],
})
state => {
let runList = [...state.runs]

const index = runList.findIndex(it => it.id === run.id)
if (index > -1) {
// ws message update has arrived earlier

const existingRun = runList[index]
runList[index] = {
...run,
status: existingRun.status,
finishedAt: existingRun.finishedAt,
}
} else {
runList = [run, ...runList]
}

return {
...state,
viewState: 'run-list',
runs: runList,
}
}

export const upsertEventWatcher =
(eventWatcher: PipelineEventWatcher): RepatchAction<PipelineDetailsState> =>
Expand Down
2 changes: 1 addition & 1 deletion web/crux-ui/src/models/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export type CreatePipeline = UpdatePipeline & {
}

export type TriggerPipeline = {
inputs?: Record<string, string>
inputs?: UniqueKeyValue[]
}

export const WS_TYPE_PIPELINE_STATUS = 'pipeline-status'
Expand Down
16 changes: 12 additions & 4 deletions web/crux/src/domain/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ export type AzureDevOpsVariable = {
value: string
}

export type AzureDevOpsPipelineTrigger = {
variables: Record<string, AzureDevOpsVariable>
}

export type AzureDevOpsPipelineTriggerError = {
message: string
}

export type AzureDevOpsPipeline = {
id: number
name: string
Expand Down Expand Up @@ -121,14 +129,14 @@ export type PipelineCreateRunOptions = {
}
)

const applyTemplate = (template: string, value: string, candidate: string): string =>
candidate.replace(new RegExp(`{{\\s*${template}\\s*}}`), value)
const applyTemplate = (template: string, inputValue: string, templateValue: string): string =>
inputValue.replace(new RegExp(`{{\\s*${template}\\s*}}`), templateValue)

const applyTemplatesOnInput = (input: UniqueKeyValue, templates: Record<string, string>) => {
Object.entries(templates).forEach(entry => {
const [template, value] = entry
const [template, templateValue] = entry

input.value = applyTemplate(template, value, input.key)
input.value = applyTemplate(template, input.value, templateValue)
})
}

Expand Down
18 changes: 17 additions & 1 deletion web/crux/src/services/azure-devops.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import {
AzureDevOpsHook,
AzureDevOpsListResponse,
AzureDevOpsPipeline,
AzureDevOpsPipelineTrigger,
AzureDevOpsPipelineTriggerError,
AzureDevOpsProject,
AzureDevOpsRun,
AzureDevOpsVariable,
PipelineHookOptions,
} from 'src/domain/pipeline'
import {
CruxBadRequestException,
CruxForbiddenException,
CruxInternalServerErrorException,
CruxNotFoundException,
Expand Down Expand Up @@ -110,8 +113,21 @@ export default class AzureDevOpsService {
return result
}, {})

const res = await this.post(creds, `/pipelines/${pipeline.id}/runs`, variables)
const body: AzureDevOpsPipelineTrigger = {
variables,
}

const res = await this.post(creds, `/pipelines/${pipeline.id}/runs`, body)
if (!res.ok) {
if (res.status === 400) {
const error = (await res.json()) as AzureDevOpsPipelineTriggerError

throw new CruxBadRequestException({
message: error.message,
})
}

this.logger.error(`Failed to start AzureDevOps pipeline. Status: ${res.status}, ${await res.text()}`)
throw new CruxInternalServerErrorException({
message: 'Failed to start pipeline',
property: 'httpStatus',
Expand Down

0 comments on commit 0d2e7a9

Please sign in to comment.