import 'brace/mode/json'; import React from 'react'; import { Modal } from 'react-bootstrap'; import type { IPipeline, IPipelineLock, IStage } from '../../../../domain'; import type { IModalComponentProps } from '../../../../presentation'; import { JsonEditor } from '../../../../presentation'; import { PipelineJSONService } from '../../services/pipelineJSON.service'; import { JsonUtils, noop } from '../../../../utils'; export interface IEditPipelineJsonModalProps extends IModalComponentProps { pipeline: IPipeline; plan: IPipeline; } export interface IEditPipelineJsonModalState { errorMessage?: string; pipelineJSON: string; pipelinePlanJSON?: string; locked: IPipelineLock; isStrategy: boolean; activeTab: mode; } type mode = 'pipeline' | 'renderedPipeline'; export class EditPipelineJsonModal extends React.Component { public static defaultProps: Partial = { closeModal: noop, dismissModal: noop, }; constructor(props: IEditPipelineJsonModalProps) { super(props); const copy = PipelineJSONService.clone(props.pipeline); let copyPlan: IPipeline; if (props.plan) { copyPlan = PipelineJSONService.clone(props.plan); } this.state = { pipelineJSON: JsonUtils.makeSortedStringFromObject(copy), pipelinePlanJSON: copyPlan ? JsonUtils.makeSortedStringFromObject(copyPlan) : null, locked: copy.locked, isStrategy: copy.strategy || false, activeTab: 'pipeline', }; } private removeImmutableFields(pipeline: IPipeline): void { // no index signature on pipeline PipelineJSONService.immutableFields.forEach((k) => delete (pipeline as any)[k]); } private validatePipeline(pipeline: IPipeline): void { const refIds = new Set(); const badIds = new Set(); if (pipeline.stages) { pipeline.stages.forEach((stage: IStage) => { if (refIds.has(stage.refId)) { badIds.add(stage.refId); } refIds.add(stage.refId); }); if (badIds.size) { throw new Error( `The refId property must be unique across stages. Duplicate id(s): ${Array.from(badIds).toString()}`, ); } } } private updatePipeline = (): void => { const { pipelineJSON } = this.state; const { pipeline } = this.props; try { const parsed = JSON.parse(pipelineJSON); parsed.appConfig = parsed.appConfig || {}; this.validatePipeline(parsed); Object.keys(pipeline) .filter((k) => !PipelineJSONService.immutableFields.has(k) && !parsed.hasOwnProperty(k)) .forEach((k) => delete (pipeline as any)[k]); this.removeImmutableFields(parsed); Object.assign(pipeline, parsed); this.props.closeModal(); } catch (e) { this.setState({ errorMessage: e.message }); } }; private setActiveTab(activeTab: mode) { this.setState({ activeTab }); } private updateJson = (pipelineJSON: string) => { this.setState({ pipelineJSON }); }; private onValidate = (errorMessage: string) => { this.setState({ errorMessage }); }; public render() { const { pipelineJSON, pipelinePlanJSON, locked, isStrategy, activeTab, errorMessage } = this.state; const invalid = !!errorMessage; const { dismissModal } = this.props; return ( <> {!locked && Edit } {isStrategy ? 'Strategy' : 'Pipeline'} JSON {!!pipelinePlanJSON && ( )} {activeTab === 'pipeline' && ( <>

The JSON below represents the {isStrategy ? 'strategy' : 'pipeline'} configuration in its persisted state.

{!locked && (

Note: Clicking "Update {isStrategy ? 'Strategy' : 'Pipeline'}" below will not save your changes to the server - it only updates the configuration within the browser, so you'll want to verify your changes and click "Save Changes" when you're ready.

)} )} {activeTab === 'renderedPipeline' && ( <>

This pipeline is based on a template. The JSON below represents the rendered pipeline.

)}
{invalid && (
Error: {errorMessage}
)} {!locked && ( )}
); } }