Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(pipeline): update pipeline json edit check
* update pipeline validation to check for duplicate `refId` values when the user edits the raw pipeline JSON. * update the edit pipeline json controller to typescript * convert corresponding test to typescript.
- Loading branch information
Showing
8 changed files
with
200 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 0 additions & 54 deletions
54
app/scripts/modules/core/pipeline/config/actions/json/editPipelineJson.module.js
This file was deleted.
Oops, something went wrong.
107 changes: 0 additions & 107 deletions
107
...cripts/modules/core/pipeline/config/actions/json/editPipelineJsonModal.controller.spec.js
This file was deleted.
Oops, something went wrong.
92 changes: 92 additions & 0 deletions
92
...cripts/modules/core/pipeline/config/actions/json/editPipelineJsonModal.controller.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import {mock, IControllerService} from 'angular'; | ||
|
||
import {EDIT_PIPELINE_JSON_MODAL_CONTROLLER, EditPipelineJsonModalCtrl} from './editPipelineJsonModal.controller'; | ||
import {IPipeline} from 'core/domain'; | ||
|
||
describe('Controller: editPipelineJsonModal', () => { | ||
|
||
let $ctrl: IControllerService, | ||
controller: EditPipelineJsonModalCtrl, | ||
$uibModalInstance: any; | ||
beforeEach(mock.module(EDIT_PIPELINE_JSON_MODAL_CONTROLLER)); | ||
beforeEach(mock.inject(($controller: IControllerService) => $ctrl = $controller)); | ||
|
||
function initializeController(pipeline: IPipeline) { | ||
$uibModalInstance = {close: () => {}}; | ||
controller = $ctrl(EditPipelineJsonModalCtrl, {$uibModalInstance, pipeline}); | ||
} | ||
|
||
it('controller removes name, application, appConfig, all fields and hash keys', () => { | ||
|
||
const pipeline: any = { | ||
name: 'foo', | ||
application: 'myApp', | ||
appConfig: 'appConfig', | ||
stage: { | ||
foo: [{}], | ||
bar: {}, | ||
baz: '', | ||
bat: 4 | ||
} | ||
}; | ||
initializeController(pipeline); | ||
|
||
// sprinkle some hash keys into the pipeline | ||
pipeline.stage.$$hashKey = '01D'; | ||
pipeline.stage.foo[0].$$hashKey = '01F'; | ||
pipeline.stage.bar.$$hashKey = '01G'; | ||
pipeline.plain = () => pipeline; | ||
controller.$onInit(); | ||
|
||
const converted: any = JSON.parse(controller.command.pipelineJSON); | ||
expect(converted.name).toBeUndefined(); | ||
expect(converted.application).toBeUndefined(); | ||
expect(converted.appConfig).toBe('appConfig'); | ||
|
||
expect(converted.stage.$$hashKey).toBeUndefined(); | ||
expect(converted.stage.foo[0].$$hashKey).toBeUndefined(); | ||
expect(converted.stage.bar.$$hashKey).toBeUndefined(); | ||
}); | ||
|
||
it('updatePipeline updates fields, removing name if added', () => { | ||
|
||
const pipeline: any = { | ||
application: 'myApp', | ||
name: 'foo', | ||
stage: { | ||
foo: [ | ||
{} | ||
], | ||
bar: {}, | ||
baz: '', | ||
bat: 4 | ||
} | ||
}; | ||
initializeController(pipeline); | ||
controller.$onInit(); | ||
spyOn($uibModalInstance, 'close'); | ||
|
||
const converted: any = JSON.parse(controller.command.pipelineJSON); | ||
converted.application = 'someOtherApp'; | ||
converted.name = 'replacedName'; | ||
converted.bar = {updated: true}; | ||
controller.command.pipelineJSON = JSON.stringify(converted); | ||
controller.updatePipeline(); | ||
|
||
expect(pipeline.application).toBe('myApp'); | ||
expect(pipeline.bar.updated).toBe(true); | ||
expect($uibModalInstance.close).toHaveBeenCalled(); | ||
}); | ||
|
||
it('updatePipeline generates an error message when malformed JSON provided', () => { | ||
|
||
const pipeline: any = {}; | ||
initializeController(pipeline); | ||
|
||
controller.command = {pipelineJSON: 'This is not very good JSON', locked: false}; | ||
controller.updatePipeline(); | ||
|
||
expect(controller.command.invalid).toBe(true); | ||
expect(controller.command.errorMessage).not.toBe(null); | ||
}); | ||
}); |
87 changes: 87 additions & 0 deletions
87
app/scripts/modules/core/pipeline/config/actions/json/editPipelineJsonModal.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import {cloneDeepWith} from 'lodash'; | ||
import {extend, module, IComponentController} from 'angular'; | ||
import {IModalServiceInstance} from 'angular-ui-bootstrap'; | ||
|
||
import {JSON_UTILITY_SERVICE, JsonUtilityService} from 'core/utils/json/json.utility.service'; | ||
import {IPipeline, IStage} from 'core/domain'; | ||
|
||
interface ICommand { | ||
errorMessage?: string; | ||
invalid?: boolean; | ||
pipelineJSON: string; | ||
locked: boolean; | ||
} | ||
|
||
export class EditPipelineJsonModalCtrl implements IComponentController { | ||
|
||
public isStrategy: boolean; | ||
public command: ICommand; | ||
|
||
static get $inject(): string[] { | ||
return ['$uibModalInstance', 'jsonUtilityService', 'pipeline']; | ||
} | ||
|
||
constructor(private $uibModalInstance: IModalServiceInstance, | ||
private jsonUtilityService: JsonUtilityService, | ||
private pipeline: IPipeline) {} | ||
|
||
private removeImmutableFields(pipeline: IPipeline): void { | ||
delete pipeline.name; | ||
delete pipeline.application; | ||
delete pipeline.index; | ||
delete pipeline.id; | ||
} | ||
|
||
private validatePipeline(pipeline: IPipeline): void { | ||
|
||
const refIds = new Set<string | number>(); | ||
const badIds = new Set<string | number>(); | ||
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()}`); | ||
} | ||
} | ||
} | ||
|
||
public $onInit(): void { | ||
const copy = cloneDeepWith<IPipeline>(this.pipeline, (value: any) => { | ||
if (value && value.$$hashKey) { | ||
delete value.$$hashKey; | ||
} | ||
return undefined; // required for clone operation and typescript happiness | ||
}); | ||
this.removeImmutableFields(copy); | ||
|
||
this.isStrategy = this.pipeline.strategy || false; | ||
this.command = { | ||
pipelineJSON: this.jsonUtilityService.makeSortedStringFromObject(copy), | ||
locked: copy.locked | ||
}; | ||
} | ||
|
||
public updatePipeline(): void { | ||
try { | ||
const parsed = JSON.parse(this.command.pipelineJSON); | ||
parsed.appConfig = parsed.appConfig || {}; | ||
|
||
this.validatePipeline(parsed); | ||
|
||
this.removeImmutableFields(parsed); | ||
extend(this.pipeline, parsed); | ||
this.$uibModalInstance.close(); | ||
} catch (e) { | ||
this.command.invalid = true; | ||
this.command.errorMessage = e.message; | ||
} | ||
} | ||
} | ||
|
||
export const EDIT_PIPELINE_JSON_MODAL_CONTROLLER = 'spinnaker.core.pipeline.config.actions.editJson'; | ||
module(EDIT_PIPELINE_JSON_MODAL_CONTROLLER, [JSON_UTILITY_SERVICE]); |
Oops, something went wrong.