import type { FormikErrors, FormikProps } from 'formik'; import { FieldArray, getIn } from 'formik'; import { chain, get, isEqual } from 'lodash'; import React from 'react'; import type { IPipeline, IProject, IProjectPipeline } from '../../domain'; import type { IWizardPageComponent } from '../../modal'; import { PipelineConfigService } from '../../pipeline'; import { FormikFormField, ReactSelectInput, StringsAsOptions } from '../../presentation'; import { Spinner } from '../../widgets'; export interface IPipelinesProps { formik: FormikProps; } export interface IPipelinesState { appsPipelines: { [appName: string]: IPipeline[]; }; initialized: boolean; } export class Pipelines extends React.Component implements IWizardPageComponent { private static readonly pipelineConfigsPath = 'config.pipelineConfigs'; public state: IPipelinesState = { appsPipelines: {}, initialized: false, }; public validate = (value: IProject): FormikErrors => { const projectApplications = (value.config && value.config.applications) || []; const { appsPipelines, initialized } = this.state; if (initialized && value.config && value.config.pipelineConfigs && value.config.pipelineConfigs.length) { const pipelineConfigErrors = value.config.pipelineConfigs.map((config) => { const pipelineIdsForApp = appsPipelines[config.application].map((p) => p.id); if (!config.application) { return { application: 'Application must be specified' }; } else if (!projectApplications.includes(config.application)) { return { application: 'This application is not part of the project' }; } else if (!config.pipelineConfigId) { return { pipelineConfigId: 'Pipeline must be specified' }; } else if (!pipelineIdsForApp.includes(config.pipelineConfigId)) { return { pipelineConfigId: `Pipeline does not exist in ${config.application}` }; } return null; }); if (pipelineConfigErrors.some((val) => !!val)) { return { config: { pipelineConfigs: pipelineConfigErrors as any, }, }; } } return {}; }; private getProjectPipelines = (props: IPipelinesProps): IProjectPipeline[] => { return get(props.formik.values, Pipelines.pipelineConfigsPath, []); }; private fetchPipelinesForApps = (projectPipelines: IProjectPipeline[]) => { const appsToFetch = chain(projectPipelines) .map('application') .uniq() // Only fetch for apps we don't already have results for .filter((appName) => appName && !this.state.appsPipelines[appName]) .value(); const appsPipelines: { [appName: string]: IPipeline[] } = { ...this.state.appsPipelines }; Promise.all( appsToFetch.map((appName) => { return PipelineConfigService.getPipelinesForApplication(appName) .then((pipelines) => { appsPipelines[appName] = pipelines; }) .catch(() => { appsPipelines[appName] = []; }); }), ).then(() => { this.setState({ appsPipelines, initialized: true }); }); }; public componentDidMount() { this.fetchPipelinesForApps(this.getProjectPipelines(this.props)); } public componentDidUpdate(prevProps: IPipelinesProps) { if (!isEqual(this.getProjectPipelines(prevProps), this.getProjectPipelines(this.props))) { this.fetchPipelinesForApps(this.getProjectPipelines(this.props)); } } public render() { const { appsPipelines, initialized } = this.state; if (!initialized) { return (
); } const tableHeader = ( App Pipeline ); return ( { const project: IProject = pipelinesArrayHelper.form.values; const configs: IProjectPipeline[] = getIn(project, Pipelines.pipelineConfigsPath); const apps: string[] = getIn(project, 'config.applications'); return (
{tableHeader} {configs.map((config, idx) => { const pipelinePath = `${Pipelines.pipelineConfigsPath}[${idx}]`; const application = config && config.application; const appPipelines = application && appsPipelines[application]; const pipelineOptions = appPipelines && appPipelines.map((p) => ({ label: p.name, value: p.id })); const key = `${application}-${config && config.pipelineConfigId}-${idx}`; return ( ); })}
{input}
} input={(props) => ( {(options) => } )} />
{!application ? null : !pipelineOptions ? ( ) : (
{input}
} input={(props) => ( )} /> )}
pipelinesArrayHelper.push({})} > Add Pipeline
); }} /> ); } }