Skip to content

Commit

Permalink
fix(pipeline): update pipeline json edit check
Browse files Browse the repository at this point in the history
* 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
icfantv committed Mar 8, 2017
1 parent e846e42 commit a8660d0
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 177 deletions.
1 change: 1 addition & 0 deletions app/scripts/modules/core/domain/IPipeline.ts
Expand Up @@ -9,6 +9,7 @@ export interface IPipeline {
isNew?: boolean;
keepWaitingPipelines: boolean;
lastModifiedBy?: string;
locked?: boolean;
limitConcurrent: boolean;
name: string;
parallel: boolean;
Expand Down
Expand Up @@ -2,9 +2,11 @@

let angular = require('angular');

import {EDIT_PIPELINE_JSON_MODAL_CONTROLLER} from './json/editPipelineJsonModal.controller';

module.exports = angular.module('spinnaker.core.pipeline.config.actions', [
require('./delete/delete.module.js'),
require('./json/editPipelineJson.module.js'),
EDIT_PIPELINE_JSON_MODAL_CONTROLLER,
require('./rename/rename.module.js'),
require('./history/showHistory.controller'),
require('./enable/enable.module'),
Expand Down

This file was deleted.

This file was deleted.

@@ -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);
});
});
@@ -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]);

0 comments on commit a8660d0

Please sign in to comment.