From 0a6d3f92733736c6ee75d679971afc3eb62bb33f Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 24 Oct 2018 16:34:53 -0400 Subject: [PATCH] feat(provider/appengine): enable artifacts as config files (#5888) --- .../serverGroupCommandBuilder.service.ts | 2 + .../wizard/configFiles.component.html | 40 ++++++++++ .../configure/wizard/configFiles.component.ts | 68 ++++++++++++++++- .../configure/wizard/serverGroupWizard.less | 19 ++++- .../appengine/src/serverGroup/transformer.ts | 4 +- .../core/src/artifact/IArtifactAccountPair.ts | 4 + .../NgAppengineConfigArtifactDelegate.ts | 75 +++++++++++++++++++ .../modules/core/src/artifact/index.ts | 2 + .../artifact/react/ExpectedArtifactEditor.tsx | 13 ++-- .../core/src/domain/IArtifactEditorProps.ts | 1 + .../bakeManifestConfig.controller.ts | 26 +++---- .../stageConfigField/StageConfigField.tsx | 3 +- .../artifacts/custom/CustomArtifactEditor.tsx | 22 +++--- .../artifacts/singleFieldArtifactEditor.tsx | 8 +- 14 files changed, 250 insertions(+), 37 deletions(-) create mode 100644 app/scripts/modules/core/src/artifact/IArtifactAccountPair.ts create mode 100644 app/scripts/modules/core/src/artifact/NgAppengineConfigArtifactDelegate.ts diff --git a/app/scripts/modules/appengine/src/serverGroup/configure/serverGroupCommandBuilder.service.ts b/app/scripts/modules/appengine/src/serverGroup/configure/serverGroupCommandBuilder.service.ts index 8bd23118058..7767b2119e0 100644 --- a/app/scripts/modules/appengine/src/serverGroup/configure/serverGroupCommandBuilder.service.ts +++ b/app/scripts/modules/appengine/src/serverGroup/configure/serverGroupCommandBuilder.service.ts @@ -9,6 +9,7 @@ import { IGitTrigger, IPipeline, IStage, + IArtifactAccountPair, } from '@spinnaker/core'; import { @@ -28,6 +29,7 @@ export interface IAppengineServerGroupCommand { freeFormDetails?: string; configFilepaths?: string[]; configFiles?: string[]; + configArtifacts?: IArtifactAccountPair[]; applicationDirectoryRoot: string; branch?: string; repositoryUrl?: string; diff --git a/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.html b/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.html index c78361fb90d..13f22a66253 100644 --- a/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.html +++ b/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.html @@ -56,5 +56,45 @@ Add Config File +
+
+ + +
+ + +
+
+ + +
+ +
+
+
+ +
diff --git a/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.ts b/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.ts index d98f0387c6b..36af314b0d6 100644 --- a/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.ts +++ b/app/scripts/modules/appengine/src/serverGroup/configure/wizard/configFiles.component.ts @@ -1,23 +1,68 @@ -import { IController, module } from 'angular'; +import { module, IController, IScope } from 'angular'; import { AppengineSourceType } from '../serverGroupCommandBuilder.service'; +import { + AccountService, + ExpectedArtifactSelectorViewController, + NgAppengineConfigArtifactDelegate, + IArtifactAccount, + IArtifactAccountPair, +} from '@spinnaker/core'; + +import './serverGroupWizard.less'; + +export interface IAppengineConfigFileConfigurerCtrlCommand { + configFiles: string[]; + configArtifacts: ConfigArtifact[]; + sourceType: string; +} class AppengineConfigFileConfigurerCtrl implements IController { - public command: { configFiles: string[]; sourceType: string }; + private artifactAccounts: IArtifactAccount[] = []; + public command: IAppengineConfigFileConfigurerCtrlCommand; + + constructor(public $scope: IScope) {} public $onInit(): void { if (!this.command.configFiles) { this.command.configFiles = []; } + if (!this.command.configArtifacts) { + this.command.configArtifacts = []; + } + if (!this.$scope.command) { + this.$scope.command = this.command; + } + this.command.configArtifacts = this.command.configArtifacts.map(artifactAccountPair => { + return new ConfigArtifact(this.$scope, artifactAccountPair); + }); + AccountService.getArtifactAccounts().then((accounts: IArtifactAccount[]) => { + this.artifactAccounts = accounts; + this.command.configArtifacts.forEach((a: ConfigArtifact) => { + a.delegate.setAccounts(accounts); + a.controller.updateAccounts(a.delegate.getSelectedExpectedArtifact()); + }); + }); } public addConfigFile(): void { this.command.configFiles.push(''); } + public addConfigArtifact(): void { + const artifact = new ConfigArtifact(this.$scope, { id: '', account: '' }); + artifact.delegate.setAccounts(this.artifactAccounts); + artifact.controller.updateAccounts(artifact.delegate.getSelectedExpectedArtifact()); + this.command.configArtifacts.push(artifact); + } + public deleteConfigFile(index: number): void { this.command.configFiles.splice(index, 1); } + public deleteConfigArtifact(index: number): void { + this.command.configArtifacts.splice(index, 1); + } + public mapTabToSpaces(event: any) { if (event.which === 9) { event.preventDefault(); @@ -33,6 +78,25 @@ class AppengineConfigFileConfigurerCtrl implements IController { } } +class ConfigArtifact implements IArtifactAccountPair { + public $scope: IScope; + public controller: ExpectedArtifactSelectorViewController; + public delegate: NgAppengineConfigArtifactDelegate; + public id: string; + public account: string; + + constructor($scope: IScope, artifact = { id: '', account: '' }) { + const unserializable = { configurable: false, enumerable: false, writable: false }; + this.id = artifact.id; + this.account = artifact.account; + Object.defineProperty(this, '$scope', { ...unserializable, value: $scope }); + const delegate = new NgAppengineConfigArtifactDelegate(this); + const controller = new ExpectedArtifactSelectorViewController(delegate); + Object.defineProperty(this, 'delegate', { ...unserializable, value: delegate }); + Object.defineProperty(this, 'controller', { ...unserializable, value: controller }); + } +} + class AppengineConfigFileConfigurerComponent implements ng.IComponentOptions { public bindings: any = { command: '=' }; public controller: any = AppengineConfigFileConfigurerCtrl; diff --git a/app/scripts/modules/appengine/src/serverGroup/configure/wizard/serverGroupWizard.less b/app/scripts/modules/appengine/src/serverGroup/configure/wizard/serverGroupWizard.less index ff8853370d6..f49077a5abc 100644 --- a/app/scripts/modules/appengine/src/serverGroup/configure/wizard/serverGroupWizard.less +++ b/app/scripts/modules/appengine/src/serverGroup/configure/wizard/serverGroupWizard.less @@ -1,6 +1,6 @@ .appengine-server-group-wizard { - input[type="checkbox"] { - margin-top: .75rem; + input[type='checkbox'] { + margin-top: 0.75rem; } help-field.help-field-absolute { @@ -10,4 +10,19 @@ right: 2px; } } + + .artifact-configuration-section { + border-bottom: 1px solid #eee; + padding: 0 0 4px 0; + margin: 0 0 5px 0; + + &.last-entry { + border-bottom: none; + } + + .Select, + input { + margin: 0 0 1px; + } + } } diff --git a/app/scripts/modules/appengine/src/serverGroup/transformer.ts b/app/scripts/modules/appengine/src/serverGroup/transformer.ts index f9d3e1e8101..54dd2117708 100644 --- a/app/scripts/modules/appengine/src/serverGroup/transformer.ts +++ b/app/scripts/modules/appengine/src/serverGroup/transformer.ts @@ -1,6 +1,6 @@ import { module } from 'angular'; -import { IServerGroup } from '@spinnaker/core'; +import { IServerGroup, IArtifactAccountPair } from '@spinnaker/core'; import { GitCredentialType, IAppengineGitTrigger, IAppengineJenkinsTrigger } from 'appengine/domain/index'; import { IAppengineServerGroupCommand } from './configure/serverGroupCommandBuilder.service'; @@ -17,6 +17,7 @@ export class AppengineDeployDescription { public branch: string; public configFilepaths: string[]; public configFiles: string[]; + public configArtifacts: IArtifactAccountPair[]; public applicationDirectoryRoot: string; public promote?: boolean; public stopPreviousVersion?: boolean; @@ -55,6 +56,7 @@ export class AppengineDeployDescription { this.trigger = command.trigger; this.gitCredentialType = command.gitCredentialType; this.configFiles = command.configFiles; + this.configArtifacts = command.configArtifacts.filter(a => !!a.id); this.applicationDirectoryRoot = command.applicationDirectoryRoot; this.interestingHealthProviderNames = command.interestingHealthProviderNames || []; this.expectedArtifactId = command.expectedArtifactId; diff --git a/app/scripts/modules/core/src/artifact/IArtifactAccountPair.ts b/app/scripts/modules/core/src/artifact/IArtifactAccountPair.ts new file mode 100644 index 00000000000..85b0f807c3d --- /dev/null +++ b/app/scripts/modules/core/src/artifact/IArtifactAccountPair.ts @@ -0,0 +1,4 @@ +export interface IArtifactAccountPair { + id: string; + account: string; +} diff --git a/app/scripts/modules/core/src/artifact/NgAppengineConfigArtifactDelegate.ts b/app/scripts/modules/core/src/artifact/NgAppengineConfigArtifactDelegate.ts new file mode 100644 index 00000000000..9833e26261b --- /dev/null +++ b/app/scripts/modules/core/src/artifact/NgAppengineConfigArtifactDelegate.ts @@ -0,0 +1,75 @@ +import { IScope } from 'angular'; +import { get } from 'lodash'; +import { + ExpectedArtifactService, + Registry, + IExpectedArtifact, + IArtifactAccount, + IArtifactSource, + IExpectedArtifactSelectorViewControllerDelegate, + IStage, + IPipeline, +} from 'core'; +import { ExpectedArtifactSelectorViewControllerAngularDelegate } from './ExpectedArtifactSelectorViewControllerAngularDelegate'; + +export class NgAppengineConfigArtifactDelegate + extends ExpectedArtifactSelectorViewControllerAngularDelegate> + implements IExpectedArtifactSelectorViewControllerDelegate { + public expectedArtifacts: IExpectedArtifact[]; + public requestingNew = false; + + private getStage() { + return get(this, 'artifact.$scope.command.viewState.stage', null); + } + + private getPipeline() { + return get(this, 'artifact.$scope.command.viewState.pipeline', null); + } + + constructor(private artifact: { $scope: IScope; id: string; account: string }) { + super(artifact.$scope); + this.refreshExpectedArtifacts(); + this.sources = ExpectedArtifactService.sourcesForPipelineStage(() => this.getPipeline(), this.getStage()); + this.kinds = Registry.pipeline.getArtifactKinds().filter(a => a.isMatch); + } + + public setAccounts = (accounts: any) => { + this.accounts = [...accounts]; + }; + + public getExpectedArtifacts(): IExpectedArtifact[] { + return ExpectedArtifactService.getExpectedArtifactsAvailableToStage(this.getStage(), this.getPipeline()); + } + + public getSelectedExpectedArtifact(): IExpectedArtifact { + return this.expectedArtifacts.find(ea => ea.id === this.artifact.id); + } + + public getSelectedAccount(): IArtifactAccount { + return this.accounts.find(a => a.name === this.artifact.account); + } + + public setSelectedExpectedArtifact(e: IExpectedArtifact): void { + this.artifact.id = e.id; + this.requestingNew = false; + this.scopeApply(); + } + + public setSelectedArtifactAccount(a: IArtifactAccount): void { + if (a == null) { + this.artifact.account = ''; + } else { + this.artifact.account = a.name; + } + this.scopeApply(); + } + + public createArtifact(): void { + this.requestingNew = true; + this.scopeApply(); + } + + public refreshExpectedArtifacts(): void { + this.expectedArtifacts = this.getExpectedArtifacts(); + } +} diff --git a/app/scripts/modules/core/src/artifact/index.ts b/app/scripts/modules/core/src/artifact/index.ts index 53fff5fd6d4..9b3711e665d 100644 --- a/app/scripts/modules/core/src/artifact/index.ts +++ b/app/scripts/modules/core/src/artifact/index.ts @@ -12,3 +12,5 @@ export * from './NgManifestArtifactDelegate'; export * from './NgGCEImageArtifactDelegate'; export * from './NgAppEngineDeployArtifactDelegate'; export * from './NgBakeManifestArtifactDelegate'; +export * from './IArtifactAccountPair'; +export * from './NgAppengineConfigArtifactDelegate'; diff --git a/app/scripts/modules/core/src/artifact/react/ExpectedArtifactEditor.tsx b/app/scripts/modules/core/src/artifact/react/ExpectedArtifactEditor.tsx index 4c56956486c..a8bd4588b53 100644 --- a/app/scripts/modules/core/src/artifact/react/ExpectedArtifactEditor.tsx +++ b/app/scripts/modules/core/src/artifact/react/ExpectedArtifactEditor.tsx @@ -25,6 +25,7 @@ export interface IExpectedArtifactEditorProps { showIcons?: boolean; showAccounts?: boolean; className?: string; + fieldGroupClassName?: string; fieldColumns: number; singleColumn: boolean; } @@ -114,7 +115,7 @@ export class ExpectedArtifactEditor extends React.Component< }; public render() { - const { sources, showIcons, showAccounts, fieldColumns, singleColumn } = this.props; + const { sources, showIcons, showAccounts, fieldColumns, singleColumn, fieldGroupClassName } = this.props; const { expectedArtifact, source, account } = this.state; const accounts = this.accountsForExpectedArtifact(expectedArtifact); const artifact = ExpectedArtifactService.artifactFromExpected(expectedArtifact); @@ -123,10 +124,10 @@ export class ExpectedArtifactEditor extends React.Component< const EditCmp = kind && kind.editCmp; return ( <> - + - + {showAccounts && ( - + )} @@ -146,9 +147,10 @@ export class ExpectedArtifactEditor extends React.Component< labelColumns={3} fieldColumns={fieldColumns} singleColumn={singleColumn} + groupClassName={fieldGroupClassName} /> )} - + @@ -175,5 +177,6 @@ module(EXPECTED_ARTIFACT_EDITOR_COMPONENT_REACT, [ 'className', 'fieldColumns', 'singleColumn', + 'fieldGroupClassName', ]), ); diff --git a/app/scripts/modules/core/src/domain/IArtifactEditorProps.ts b/app/scripts/modules/core/src/domain/IArtifactEditorProps.ts index 8c571e5551a..d04e5b5eb93 100644 --- a/app/scripts/modules/core/src/domain/IArtifactEditorProps.ts +++ b/app/scripts/modules/core/src/domain/IArtifactEditorProps.ts @@ -6,4 +6,5 @@ export interface IArtifactEditorProps { labelColumns: number; fieldColumns: number; singleColumn?: boolean; + groupClassName?: string; } diff --git a/app/scripts/modules/core/src/pipeline/config/stages/bakeManifest/bakeManifestConfig.controller.ts b/app/scripts/modules/core/src/pipeline/config/stages/bakeManifest/bakeManifestConfig.controller.ts index d06c2d1fa9e..d45b8f073d1 100644 --- a/app/scripts/modules/core/src/pipeline/config/stages/bakeManifest/bakeManifestConfig.controller.ts +++ b/app/scripts/modules/core/src/pipeline/config/stages/bakeManifest/bakeManifestConfig.controller.ts @@ -5,14 +5,10 @@ import { ExpectedArtifactSelectorViewController, NgBakeManifestArtifactDelegate, IArtifactAccount, + IArtifactAccountPair, } from 'core'; import { UUIDGenerator } from 'core/utils'; -export interface IInputArtifact { - id: string; - account: string; -} - export class BakeManifestConfigCtrl implements IController { public artifactControllers: any[]; public artifactAccounts: IArtifactAccount[] = []; @@ -49,7 +45,7 @@ export class BakeManifestConfigCtrl implements IController { Object.assign(stage, defaultSelection); } this.ensureTemplateArtifact(); - stage.inputArtifacts = stage.inputArtifacts.map((a: IInputArtifact) => this.defaultInputArtifact(a)); + stage.inputArtifacts = stage.inputArtifacts.map((a: IArtifactAccountPair) => this.defaultInputArtifact(a)); AccountService.getArtifactAccounts().then(accounts => { this.artifactAccounts = accounts; stage.inputArtifacts.forEach((a: InputArtifact) => { @@ -111,21 +107,21 @@ export class BakeManifestConfigCtrl implements IController { } } -class InputArtifact { +class InputArtifact implements IArtifactAccountPair { + public $scope: IScope; public controller: ExpectedArtifactSelectorViewController; public delegate: NgBakeManifestArtifactDelegate; public id: string; public account: string; - constructor(public $scope: IScope, artifact = { id: '', account: '' }) { - setUnserializable(this, '$scope', $scope); - setUnserializable(this, 'delegate', new NgBakeManifestArtifactDelegate(this)); - setUnserializable(this, 'controller', new ExpectedArtifactSelectorViewController(this.delegate)); + constructor($scope: IScope, artifact = { id: '', account: '' }) { + const unserializable = { configurable: false, enumerable: false, writable: false }; + const delegate = new NgBakeManifestArtifactDelegate(this); + const controller = new ExpectedArtifactSelectorViewController(delegate); + Object.defineProperty(this, '$scope', { ...unserializable, value: $scope }); + Object.defineProperty(this, 'delegate', { ...unserializable, value: delegate }); + Object.defineProperty(this, 'controller', { ...unserializable, value: controller }); this.id = artifact.id; this.account = artifact.account; } } - -const setUnserializable = (obj: any, key: string, value: any) => { - return Object.defineProperty(obj, key, { configurable: false, enumerable: false, writable: false, value }); -}; diff --git a/app/scripts/modules/core/src/pipeline/config/stages/core/stageConfigField/StageConfigField.tsx b/app/scripts/modules/core/src/pipeline/config/stages/core/stageConfigField/StageConfigField.tsx index 8033e45b8c9..eaf3ba8c2c9 100644 --- a/app/scripts/modules/core/src/pipeline/config/stages/core/stageConfigField/StageConfigField.tsx +++ b/app/scripts/modules/core/src/pipeline/config/stages/core/stageConfigField/StageConfigField.tsx @@ -7,10 +7,11 @@ export interface IStageConfigFieldProps { labelColumns?: number; fieldColumns?: number; children?: React.ReactNode; + groupClassName?: string; } export const StageConfigField = (props: IStageConfigFieldProps) => ( -
+