summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimur Sufiev <tsufiev@mirantis.com>2015-07-13 13:29:27 +0300
committerTimur Sufiev <tsufiev@mirantis.com>2015-07-13 15:05:58 +0300
commitd82d48453f0b31e565ad4f8a9e074ff327e15d45 (patch)
treecd5118b8d8fca053d362b270fc94fab6d695b955
parentac6336b5e23d6a3db82c709761f72574dfdcd77e (diff)
Add Mistral js files to linter and fix linting errors
As a consequence, change how the workbookCtrl controller is used (and unit-tests for it). Change-Id: I21514ac01baa81c5a760abd9e7d0f909d89617d6
Notes
Notes (review): Verified+2: Jenkins Code-Review+2: Timur Sufiev <tsufiev@mirantis.com> Workflow+1: Timur Sufiev <tsufiev@mirantis.com> Submitted-by: Jenkins Submitted-at: Mon, 13 Jul 2015 12:46:25 +0000 Reviewed-on: https://review.openstack.org/201072 Project: stackforge/merlin Branch: refs/heads/master
-rw-r--r--extensions/mistral/static/mistral/js/mistral.init.js17
-rw-r--r--extensions/mistral/static/mistral/js/mistral.workbook.controllers.js141
-rw-r--r--extensions/mistral/static/mistral/js/mistral.workbook.models.js1099
-rw-r--r--extensions/mistral/templates/mistral/create.html34
-rw-r--r--extensions/mistral/test/js/workbook.controller.spec.js32
-rw-r--r--package.json2
6 files changed, 669 insertions, 656 deletions
diff --git a/extensions/mistral/static/mistral/js/mistral.init.js b/extensions/mistral/static/mistral/js/mistral.init.js
index 6f7b864..3e06ddf 100644
--- a/extensions/mistral/static/mistral/js/mistral.init.js
+++ b/extensions/mistral/static/mistral/js/mistral.init.js
@@ -4,10 +4,15 @@
4(function() { 4(function() {
5 'use strict'; 5 'use strict';
6 6
7 angular.module('mistral', ['merlin']) 7 angular
8 .run(['merlin.templates', function(templates) { 8 .module('mistral', ['merlin'])
9 templates.prefetch('/static/mistral/templates/fields/', 9 .run(initModule);
10 ['varlist', 'yaqllist']);
11 }])
12 10
13})(); \ No newline at end of file 11 initModule.$inject = ['merlin.templates'];
12
13 function initModule(templates) {
14 templates.prefetch('/static/mistral/templates/fields/',
15 ['varlist', 'yaqllist']);
16 }
17
18})();
diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js
index 77d15e9..f4830dd 100644
--- a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js
+++ b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js
@@ -4,82 +4,87 @@
4(function() { 4(function() {
5 'use strict'; 5 'use strict';
6 6
7 angular.module('mistral') 7 angular
8 .module('mistral')
8 .value('baseActionID', 'action') 9 .value('baseActionID', 'action')
9 .value('baseWorkflowID', 'workflow') 10 .value('baseWorkflowID', 'workflow')
10 .controller('workbookCtrl', 11 .controller('WorkbookController', WorkbookController);
11 ['$scope', 'mistral.workbook.models', '$http',
12 'baseActionID', 'baseWorkflowID',
13 function($scope, models, $http, baseActionId, baseWorkflowId) {
14 $scope.init = function(id, yaml, commitUrl, discardUrl) {
15 $scope.workbookID = id;
16 $scope.commitUrl = commitUrl;
17 $scope.discardUrl = discardUrl;
18 if ( id !== undefined ) {
19 $scope.workbook = models.Workbook.create(jsyaml.safeLoad(yaml));
20 } else {
21 $scope.workbook = models.Workbook.create({name: 'My Workbook'});
22 }
23 $scope.root = models.Root.create();
24 $scope.root.set('workbook', $scope.workbook);
25 12
26 $scope.root.set('standardActions', { 13 WorkbookController.$inject = ['mistral.workbook.models', '$http',
27 'nova.create_server': ['image', 'flavor', 'network_id'], 14 '$window', 'baseActionID', 'baseWorkflowID'];
28 'neutron.create_network': ['name', 'create_subnet'],
29 'glance.create_image': ['image_url']
30 });
31 };
32 15
33 function getNextIDSuffix(container, regexp) { 16 function WorkbookController(models, $http, $window,
34 var max = Math.max.apply(Math, container.getIDs().map(function(id) { 17 baseActionId, baseWorkflowId) {
35 var match = regexp.exec(id); 18 var vm = this;
36 return match && +match[2]; 19 vm.init = function(id, yaml, commitUrl, discardUrl) {
37 })); 20 vm.workbookID = id;
38 return max > 0 ? max + 1 : 1; 21 vm.commitUrl = commitUrl;
39 } 22 vm.discardUrl = discardUrl;
23 if (angular.isDefined(id)) {
24 vm.workbook = models.Workbook.create(jsyaml.safeLoad(yaml));
25 } else {
26 vm.workbook = models.Workbook.create({name: 'My Workbook'});
27 }
28 vm.root = models.Root.create();
29 vm.root.set('workbook', vm.workbook);
40 30
41 function getWorkbookNextIDSuffix(base) { 31 vm.root.set('standardActions', {
42 var containerName = base + 's', 32 'nova.create_server': ['image', 'flavor', 'network_id'],
43 regexp = /(workflow|action)([0-9]+)/, 33 'neutron.create_network': ['name', 'create_subnet'],
44 container = $scope.workbook.get(containerName); 34 'glance.create_image': ['image_url']
45 if ( !container ) { 35 });
46 throw 'Base should be either "action" or "workflow"!'; 36 };
47 }
48 return getNextIDSuffix(container, regexp);
49 }
50 37
51 $scope.addAction = function() { 38 function getNextIDSuffix(container, regexp) {
52 var nextSuffix = getWorkbookNextIDSuffix(baseActionId), 39 var max = Math.max.apply(Math, container.getIDs().map(function(id) {
53 newID = baseActionId + nextSuffix; 40 var match = regexp.exec(id);
54 $scope.workbook.get('actions').push( 41 return match && +match[2];
55 {name: 'Action ' + nextSuffix}, {id: newID}); 42 }));
56 }; 43 return max > 0 ? max + 1 : 1;
44 }
57 45
58 $scope.addWorkflow = function() { 46 function getWorkbookNextIDSuffix(base) {
59 var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId), 47 var containerName = base + 's';
60 newID = baseWorkflowId + nextSuffix; 48 var regexp = /(workflow|action)([0-9]+)/;
61 $scope.workbook.get('workflows').push( 49 var container = vm.workbook.get(containerName);
62 {name: 'Workflow ' + nextSuffix}, {id: newID}); 50 if ( !container ) {
63 }; 51 throw new Error('Base should be either "action" or "workflow"!');
52 }
53 return getNextIDSuffix(container, regexp);
54 }
64 55
65 $scope.commitWorkbook = function() { 56 vm.addAction = function() {
66 var data = { 57 var nextSuffix = getWorkbookNextIDSuffix(baseActionId);
67 name: $scope.workbook.get('name').get(), 58 var newID = baseActionId + nextSuffix;
68 yaml: $scope.workbook.toYAML() 59 vm.workbook.get('actions').push(
69 }; 60 {name: 'Action ' + nextSuffix}, {id: newID});
61 };
70 62
71 $http({ 63 vm.addWorkflow = function() {
72 url: $scope.commitUrl, 64 var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId);
73 method: 'POST', 65 var newID = baseWorkflowId + nextSuffix;
74 data: data 66 vm.workbook.get('workflows').push(
75 }).success(function(data, status, headers, config) { 67 {name: 'Workflow ' + nextSuffix}, {id: newID});
76 document.location = $scope.discardUrl; 68 };
77 });
78 };
79 69
80 $scope.discardWorkbook = function() { 70 vm.commitWorkbook = function() {
81 document.location = $scope.discardUrl; 71 var data = {
82 }; 72 name: vm.workbook.get('name').get(),
73 yaml: vm.workbook.toYAML()
74 };
83 75
84 }]) 76 $http({
85})(); \ No newline at end of file 77 url: vm.commitUrl,
78 method: 'POST',
79 data: data
80 }).success(function() {
81 $window.location.href = vm.discardUrl;
82 });
83 };
84
85 vm.discardWorkbook = function() {
86 $window.location.href = vm.discardUrl;
87 };
88
89 }
90})();
diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.models.js b/extensions/mistral/static/mistral/js/mistral.workbook.models.js
index 9a95df7..cad9f40 100644
--- a/extensions/mistral/static/mistral/js/mistral.workbook.models.js
+++ b/extensions/mistral/static/mistral/js/mistral.workbook.models.js
@@ -4,659 +4,662 @@
4(function() { 4(function() {
5 'use strict'; 5 'use strict';
6 6
7 angular.module('mistral') 7 angular
8 .factory('mistral.workbook.models', 8 .module('mistral')
9 ['merlin.field.models', 'merlin.panel.models', 'merlin.utils', '$http', '$q', 9 .factory('mistral.workbook.models', ModelsService);
10 function(fields, panel, utils, $http, $q) {
11 var models = {};
12 10
13 function varlistValueFactory(json, parameters) { 11 ModelsService.$inject = ['merlin.field.models', 'merlin.utils'];
14 var type = Barricade.getType(json); 12
15 if ( json === undefined || type === String ) { 13 function ModelsService(fields, utils) {
16 return fields.string.create(json, parameters); 14 var models = {};
17 } else if ( type === Array ) { 15
18 return fields.list.extend({}, { 16 function varlistValueFactory(json, parameters) {
19 '*': {'@class': fields.string} 17 var type = Barricade.getType(json);
20 }).create(json, parameters); 18 if ( angular.isUndefined(json) || type === String ) {
21 } else if ( type === Object ) { 19 return fields.string.create(json, parameters);
22 return fields.dictionary.extend({}, { 20 } else if ( type === Array ) {
23 '?': {'@class': fields.string} 21 return fields.list.extend({}, {
24 }).create(json, parameters); 22 '*': {'@class': fields.string}
25 } 23 }).create(json, parameters);
26 } 24 } else if ( type === Object ) {
25 return fields.dictionary.extend({}, {
26 '?': {'@class': fields.string}
27 }).create(json, parameters);
28 }
29 }
27 30
28 models.varlist = fields.list.extend({ 31 models.varlist = fields.list.extend({
32 create: function(json, parameters) {
33 var self = fields.list.create.call(this, json, parameters);
34 self.setType('varlist');
35 self.on('childChange', function(child, op) {
36 if ( op == 'empty' ) {
37 self.each(function(index, item) {
38 if ( child === item ) {
39 self.remove(index);
40 }
41 });
42 }
43 });
44 return self;
45 }
46 }, {
47 '*': {
48 '@class': fields.frozendict.extend({
29 create: function(json, parameters) { 49 create: function(json, parameters) {
30 var self = fields.list.create.call(this, json, parameters); 50 var self = fields.frozendict.create.call(this, json, parameters);
31 self.setType('varlist'); 51 self.on('childChange', function(child) {
32 self.on('childChange', function(child, op) { 52 if ( child.instanceof(Barricade.Enumerated) ) { // type change
33 if ( op == 'empty' ) { 53 var value = self.get('value');
34 self.each(function(index, item) { 54 switch ( child.get() ) {
35 if ( child === item ) { 55 case 'string':
36 self.remove(index); 56 self.set('value', varlistValueFactory(''));
37 } 57 break;
38 }) 58 case 'list':
59 self.set('value', varlistValueFactory(['']));
60 break;
61 case 'dictionary':
62 self.set('value', varlistValueFactory({'key1': ''}));
63 break;
64 }
65 } else if ( child.instanceof(Barricade.Arraylike) && !child.length() ) {
66 self.emit('change', 'empty');
39 } 67 }
40 }); 68 });
41 return self; 69 return self;
70 },
71 _getPrettyJSON: function() {
72 var json = fields.frozendict._getPrettyJSON.apply(this, arguments);
73 return json.value;
42 } 74 }
43 }, { 75 }, {
44 '*': { 76 'type': {
45 '@class': fields.frozendict.extend({ 77 '@class': fields.string.extend({}, {
46 create: function(json, parameters) { 78 '@enum': ['string', 'list', 'dictionary'],
47 var self = fields.frozendict.create.call(this, json, parameters); 79 '@default': 'string'
48 self.on('childChange', function(child) {
49 if ( child.instanceof(Barricade.Enumerated) ) { // type change
50 var value = self.get('value');
51 switch ( child.get() ) {
52 case 'string':
53 self.set('value', varlistValueFactory(''));
54 break;
55 case 'list':
56 self.set('value', varlistValueFactory(['']));
57 break;
58 case 'dictionary':
59 self.set('value', varlistValueFactory({'key1': ''}));
60 break;
61 }
62 } else if ( child.instanceof(Barricade.Arraylike) && !child.length() ) {
63 self.emit('change', 'empty');
64 }
65 });
66 return self;
67 },
68 _getPrettyJSON: function() {
69 var json = fields.frozendict._getPrettyJSON.apply(this, arguments);
70 return json.value;
71 }
72 }, {
73 'type': {
74 '@class': fields.string.extend({}, {
75 '@enum': ['string', 'list', 'dictionary'],
76 '@default': 'string'
77 })
78 },
79 'value': {
80 '@class': fields.wildcard,
81 '@factory': varlistValueFactory
82 }
83 }) 80 })
81 },
82 'value': {
83 '@class': fields.wildcard,
84 '@factory': varlistValueFactory
84 } 85 }
85 }); 86 })
87 }
88 });
86 89
87 models.yaqllist = fields.list.extend({ 90 models.yaqllist = fields.list.extend({
91 create: function(json, parameters) {
92 var self = fields.list.create.call(this, json, parameters);
93 self.setType('yaqllist');
94 return self;
95 }
96 }, {
97 '*': {
98 '@class': fields.frozendict.extend({}, {
99 'yaql': {
100 '@class': fields.string
101 },
102 'action': {
103 '@class': fields.string
104 }
105 })
106 }
107 });
108
109 models.Action = fields.frozendict.extend({
110 create: function(json, parameters) {
111 var self = fields.frozendict.create.call(this, json, parameters);
112 var base = self.get('base');
113 base.on('change', function(operation) {
114 var argsEntry, pos, entry;
115 if ( operation != 'id' ) {
116 pos = base._collection.getPosByID(base.get());
117 if ( pos > -1 ) {
118 entry = self.get('base-input');
119 argsEntry = base._collection.get(pos);
120 entry.resetKeys(argsEntry.toJSON());
121 }
122 }
123 });
124 return self;
125 }
126 }, {
127 'base': {
128 '@class': fields.linkedcollection.extend({
88 create: function(json, parameters) { 129 create: function(json, parameters) {
89 var self = fields.list.create.call(this, json, parameters); 130 parameters = Object.create(parameters);
90 self.setType('yaqllist'); 131 parameters.toCls = models.StandardActions;
91 return self; 132 parameters.neededCls = models.Root;
133 parameters.substitutedEntryID = 'standardActions';
134 return fields.linkedcollection.create.call(this, json, parameters);
92 } 135 }
93 }, { 136 }, {
94 '*': { 137 '@meta': {
95 '@class': fields.frozendict.extend({}, { 138 'index': 1,
96 'yaql': { 139 'row': 0
97 '@class': fields.string
98 },
99 'action': {
100 '@class': fields.string
101 }
102 })
103 } 140 }
104 }); 141 })
105 142 },
106 models.Action = fields.frozendict.extend({ 143 'base-input': {
144 '@class': fields.dictionary.extend({
107 create: function(json, parameters) { 145 create: function(json, parameters) {
108 var self = fields.frozendict.create.call(this, json, parameters), 146 var self = fields.dictionary.create.call(this, json, parameters);
109 base = self.get('base'); 147 self.setType('frozendict');
110 base.on('change', function(operation) {
111 var argsEntry, pos, entry;
112 if ( operation != 'id' ) {
113 pos = base._collection.getPosByID(base.get());
114 if ( pos > -1 ) {
115 entry = self.get('base-input');
116 argsEntry = base._collection.get(pos);
117 entry.resetKeys(argsEntry.toJSON());
118 }
119 }
120 });
121 return self; 148 return self;
122 } 149 }
123 }, { 150 }, {
124 'base': { 151 '@required': false,
125 '@class': fields.linkedcollection.extend({ 152 '?': {
126 create: function(json, parameters) { 153 '@class': fields.string.extend({}, {
127 parameters = Object.create(parameters);
128 parameters.toCls = models.StandardActions;
129 parameters.neededCls = models.Root;
130 parameters.substitutedEntryID = 'standardActions';
131 return fields.linkedcollection.create.call(this, json, parameters);
132 }
133 }, {
134 '@meta': { 154 '@meta': {
135 'index': 1,
136 'row': 0 155 'row': 0
137 } 156 }
138 }) 157 })
139 }, 158 },
140 'base-input': { 159 '@meta': {
141 '@class': fields.dictionary.extend({ 160 'index': 2,
142 create: function(json, parameters) { 161 'title': 'Base Input'
143 var self = fields.dictionary.create.call(this, json, parameters); 162 }
144 self.setType('frozendict'); 163 })
145 return self; 164 },
146 } 165 'input': {
147 }, { 166 '@class': fields.list.extend({}, {
148 '@required': false, 167 '@meta': {
149 '?': { 168 'index': 3
150 '@class': fields.string.extend({}, {
151 '@meta': {
152 'row': 0
153 }
154 })
155 },
156 '@meta': {
157 'index': 2,
158 'title': 'Base Input'
159 }
160 })
161 },
162 'input': {
163 '@class': fields.list.extend({}, {
164 '@meta': {
165 'index': 3
166 },
167 '*': {'@class': fields.string}
168 })
169 }, 169 },
170 'output': { 170 '*': {'@class': fields.string}
171 '@class': models.varlist.extend({}, { 171 })
172 '@meta': { 172 },
173 'index': 4 173 'output': {
174 } 174 '@class': models.varlist.extend({}, {
175 }) 175 '@meta': {
176 'index': 4
176 } 177 }
177 }); 178 })
179 }
180 });
178 181
179 models.Task = fields.frozendict.extend({ 182 models.Task = fields.frozendict.extend({
180 create: function(json, parameters) { 183 create: function(json, parameters) {
181 var self = fields.frozendict.create.call(this, json, parameters); 184 var self = fields.frozendict.create.call(this, json, parameters);
182 self.on('childChange', function(child, op) { 185 self.on('childChange', function(child, op) {
183 if ( child === self.get('type') && op !== 'id' ) { 186 if ( child === self.get('type') && op !== 'id' ) {
184 self.emit('change', 'taskType'); 187 self.emit('change', 'taskType');
185 } 188 }
186 }); 189 });
187 return self; 190 return self;
191 },
192 remove: function() {
193 this.emit('change', 'taskRemove', this.getID());
194 },
195 _getPrettyJSON: function() {
196 var json = fields.frozendict._getPrettyJSON.apply(this, arguments);
197 delete json.type;
198 return json;
199 }
200 }, {
201 '@meta': {
202 'baseKey': 'task',
203 'baseName': 'Task ',
204 'group': true,
205 'additive': false,
206 'removable': true
207 },
208 'type': {
209 '@class': fields.string.extend({}, {
210 '@enum': [{
211 value: 'action', label: 'Action-based'
212 }, {
213 value: 'workflow', label: 'Workflow-based'
214 }],
215 '@default': 'action',
216 '@meta': {
217 'index': 0,
218 'row': 0
219 }
220 })
221 },
222 'description': {
223 '@class': fields.text.extend({}, {
224 '@meta': {
225 'index': 2,
226 'row': 1
227 }
228 })
229 },
230 'input': {
231 '@class': fields.dictionary.extend({}, {
232 '@meta': {
233 'index': 4
188 }, 234 },
189 remove: function() { 235 '?': {
190 this.emit('change', 'taskRemove', this.getID()); 236 '@class': fields.string
237 }
238 })
239 },
240 'publish': {
241 '@class': fields.dictionary.extend({}, {
242 '@meta': {
243 'index': 5
191 }, 244 },
245 '?': {
246 '@class': fields.string
247 }
248 })
249 },
250 'policies': {
251 '@class': fields.frozendict.extend({
192 _getPrettyJSON: function() { 252 _getPrettyJSON: function() {
193 var json = fields.frozendict._getPrettyJSON.apply(this, arguments); 253 var json = fields.frozendict._getPrettyJSON.apply(this, arguments);
194 delete json.type; 254 json.retry = {
255 count: utils.pop(json, 'retry-count'),
256 delay: utils.pop(json, 'retry-delay'),
257 'break-on': utils.pop(json, 'retry-break-on')
258 };
195 return json; 259 return json;
196 } 260 }
197 }, { 261 }, {
198 '@meta': { 262 '@meta': {
199 'baseKey': 'task', 263 'index': 9
200 'baseName': 'Task ',
201 'group': true,
202 'additive': false,
203 'removable': true
204 }, 264 },
205 'type': { 265 '@required': false,
206 '@class': fields.string.extend({}, { 266 'wait-before': {
207 '@enum': [{ 267 '@class': fields.number.extend({}, {
208 value: 'action', label: 'Action-based' 268 '@required': false,
209 }, {
210 value: 'workflow', label: 'Workflow-based'
211 }],
212 '@default': 'action',
213 '@meta': { 269 '@meta': {
214 'index': 0, 270 'index': 0,
215 'row': 0 271 'row': 0,
272 'title': 'Wait before'
216 } 273 }
217 }) 274 })
218 }, 275 },
219 'description': { 276 'wait-after': {
220 '@class': fields.text.extend({}, { 277 '@class': fields.number.extend({}, {
278 '@required': false,
279 '@meta': {
280 'index': 1,
281 'row': 0,
282 'title': 'Wait after'
283 }
284 })
285 },
286 'timeout': {
287 '@class': fields.number.extend({}, {
288 '@required': false,
221 '@meta': { 289 '@meta': {
222 'index': 2, 290 'index': 2,
223 'row': 1 291 'row': 1
224 } 292 }
225 }) 293 })
226 }, 294 },
227 'input': { 295 'retry-count': {
228 '@class': fields.dictionary.extend({}, { 296 '@class': fields.number.extend({}, {
297 '@required': false,
229 '@meta': { 298 '@meta': {
230 'index': 4 299 'index': 3,
231 }, 300 'row': 2,
232 '?': { 301 'title': 'Retry count'
233 '@class': fields.string
234 } 302 }
235 }) 303 })
236 }, 304 },
237 'publish': { 305 'retry-delay': {
238 '@class': fields.dictionary.extend({}, { 306 '@class': fields.number.extend({}, {
307 '@required': false,
239 '@meta': { 308 '@meta': {
240 'index': 5 309 'index': 4,
241 }, 310 'row': 2,
242 '?': { 311 'title': 'Retry delay'
243 '@class': fields.string
244 } 312 }
245 }) 313 })
246 }, 314 },
247 'policies': { 315 'retry-break-on': {
248 '@class': fields.frozendict.extend({ 316 '@class': fields.number.extend({}, {
249 _getPrettyJSON: function() {
250 var json = fields.frozendict._getPrettyJSON.apply(this, arguments);
251 json.retry = {
252 count: utils.pop(json, 'retry-count'),
253 delay: utils.pop(json, 'retry-delay'),
254 'break-on': utils.pop(json, 'retry-break-on')
255 };
256 return json;
257 }
258 }, {
259 '@meta': {
260 'index': 9
261 },
262 '@required': false, 317 '@required': false,
263 'wait-before': { 318 '@meta': {
264 '@class': fields.number.extend({}, { 319 'index': 5,
265 '@required': false, 320 'row': 3,
266 '@meta': { 321 'title': 'Retry break on'
267 'index': 0,
268 'row': 0,
269 'title': 'Wait before'
270 }
271 })
272 },
273 'wait-after': {
274 '@class': fields.number.extend({}, {
275 '@required': false,
276 '@meta': {
277 'index': 1,
278 'row': 0,
279 'title': 'Wait after'
280 }
281 })
282 },
283 'timeout': {
284 '@class': fields.number.extend({}, {
285 '@required': false,
286 '@meta': {
287 'index': 2,
288 'row': 1
289 }
290 })
291 },
292 'retry-count': {
293 '@class': fields.number.extend({}, {
294 '@required': false,
295 '@meta': {
296 'index': 3,
297 'row': 2,
298 'title': 'Retry count'
299 }
300 })
301 },
302 'retry-delay': {
303 '@class': fields.number.extend({}, {
304 '@required': false,
305 '@meta': {
306 'index': 4,
307 'row': 2,
308 'title': 'Retry delay'
309 }
310 })
311 },
312 'retry-break-on': {
313 '@class': fields.number.extend({}, {
314 '@required': false,
315 '@meta': {
316 'index': 5,
317 'row': 3,
318 'title': 'Retry break on'
319 }
320 })
321 } 322 }
322 }) 323 })
323 } 324 }
324 }); 325 })
326 }
327 });
325 328
326 models.ReverseWFTask = models.Task.extend({}, { 329 models.ReverseWFTask = models.Task.extend({}, {
327 'requires': { 330 'requires': {
328 '@class': fields.string.extend({}, { 331 '@class': fields.string.extend({}, {
329 '@meta': { 332 '@meta': {
330 'row': 2, 333 'row': 2,
331 'index': 3 334 'index': 3
332 }
333 })
334 } 335 }
335 }); 336 })
337 }
338 });
336 339
337 models.DirectWFTask = models.Task.extend({}, { 340 models.DirectWFTask = models.Task.extend({}, {
338 'on-error': { 341 'on-error': {
339 '@class': fields.list.extend({}, { 342 '@class': fields.list.extend({}, {
340 '@meta': { 343 '@meta': {
341 'title': 'On error', 344 'title': 'On error',
342 'index': 6 345 'index': 6
343 },
344 '*': {
345 '@class': fields.string
346 }
347 })
348 }, 346 },
349 'on-success': { 347 '*': {
350 '@class': fields.list.extend({}, { 348 '@class': fields.string
351 '@meta': { 349 }
352 'title': 'On success', 350 })
353 'index': 7 351 },
354 }, 352 'on-success': {
355 '*': { 353 '@class': fields.list.extend({}, {
356 '@class': fields.string 354 '@meta': {
357 } 355 'title': 'On success',
358 }) 356 'index': 7
359 }, 357 },
360 'on-complete': { 358 '*': {
361 '@class': fields.list.extend({}, { 359 '@class': fields.string
362 '@meta': {
363 'title': 'On complete',
364 'index': 8
365 },
366 '*': {
367 '@class': fields.string
368 }
369 })
370 } 360 }
371 }); 361 })
362 },
363 'on-complete': {
364 '@class': fields.list.extend({}, {
365 '@meta': {
366 'title': 'On complete',
367 'index': 8
368 },
369 '*': {
370 '@class': fields.string
371 }
372 })
373 }
374 });
372 375
373 models.ActionTaskMixin = Barricade.Blueprint.create(function() { 376 models.ActionTaskMixin = Barricade.Blueprint.create(function() {
374 return this.extend({}, { 377 return this.extend({}, {
375 'action': { 378 'action': {
376 '@class': fields.linkedcollection.extend({ 379 '@class': fields.linkedcollection.extend({
377 create: function(json, parameters) { 380 create: function(json, parameters) {
378 parameters = Object.create(parameters); 381 parameters = Object.create(parameters);
379 parameters.toCls = models.Actions; 382 parameters.toCls = models.Actions;
380 parameters.neededCls = models.Workbook; 383 parameters.neededCls = models.Workbook;
381 parameters.substitutedEntryID = 'actions'; 384 parameters.substitutedEntryID = 'actions';
382 return fields.linkedcollection.create.call(this, json, parameters); 385 return fields.linkedcollection.create.call(this, json, parameters);
383 } 386 }
384 }, { 387 }, {
385 '@meta': { 388 '@meta': {
386 'row': 0, 389 'row': 0,
387 'index': 1 390 'index': 1
388 }
389 })
390 } 391 }
391 }) 392 })
392 }); 393 }
394 });
395 });
393 396
394 models.WorkflowTaskMixin = Barricade.Blueprint.create(function() { 397 models.WorkflowTaskMixin = Barricade.Blueprint.create(function() {
395 return this.extend({}, { 398 return this.extend({}, {
396 'workflow': { 399 'workflow': {
397 '@class': fields.linkedcollection.extend({ 400 '@class': fields.linkedcollection.extend({
398 create: function(json, parameters) { 401 create: function(json, parameters) {
399 parameters = Object.create(parameters); 402 parameters = Object.create(parameters);
400 parameters.toCls = models.Workflows; 403 parameters.toCls = models.Workflows;
401 parameters.neededCls = models.Workbook; 404 parameters.neededCls = models.Workbook;
402 parameters.substitutedEntryID = 'workflows'; 405 parameters.substitutedEntryID = 'workflows';
403 return fields.linkedcollection.create.call(this, json, parameters); 406 return fields.linkedcollection.create.call(this, json, parameters);
404 }
405 }, {
406 '@meta': {
407 'row': 0,
408 'index': 1
409 }
410 })
411 } 407 }
412 }); 408 }, {
413 }); 409 '@meta': {
414 410 'row': 0,
415 var taskTypes = { 411 'index': 1
416 'direct': models.DirectWFTask, 412 }
417 'reverse': models.ReverseWFTask, 413 })
418 'action': models.ActionTaskMixin,
419 'workflow': models.WorkflowTaskMixin
420 };
421
422 function TaskFactory(json, parameters) {
423 var type = json.type || 'action',
424 baseClass = taskTypes[parameters.wfType],
425 mixinClass = taskTypes[type],
426 taskClass = mixinClass.call(baseClass);
427 return taskClass.create(json, parameters);
428 } 414 }
415 });
416 });
429 417
430 models.Workflow = fields.frozendict.extend({ 418 var taskTypes = {
431 create: function(json, parameters) { 419 'direct': models.DirectWFTask,
432 var self = fields.frozendict.create.call(this, json, parameters); 420 'reverse': models.ReverseWFTask,
433 self.on('childChange', function(child, op) { 421 'action': models.ActionTaskMixin,
434 if ( child === self.get('type') && op !== 'id' ) { 422 'workflow': models.WorkflowTaskMixin
435 self.emit('change', 'workflowType'); 423 };
436 }
437 });
438 return self;
439 }
440 }, {
441 'type': {
442 '@class': fields.string.extend({}, {
443 '@enum': ['reverse', 'direct'],
444 '@default': 'direct',
445 '@meta': {
446 'index': 1,
447 'row': 0
448 }
449 })
450 },
451 'input': {
452 '@class': fields.list.extend({}, {
453 '@required': false,
454 '@meta': {
455 'index': 2
456 },
457 '*': {
458 '@class': fields.string
459 }
460 })
461 },
462 'output': {
463 '@class': fields.list.extend({}, {
464 '@required': false,
465 '@meta': {
466 'index': 3
467 },
468 '*': {
469 '@class': fields.string
470 }
471 })
472 },
473 'tasks': {
474 '@class': fields.dictionary.extend({
475 create: function(json, parameters) {
476 var self = fields.dictionary.create.call(this, json, parameters);
477 self.on('childChange', function(child, op, arg) {
478 if ( op === 'taskType' ) {
479 var taskId = child.getID(),
480 params = child._parameters,
481 taskPos = self.getPosByID(taskId),
482 taskData = child.toJSON();
483 params.id = taskId;
484 self.set(taskPos, TaskFactory(taskData, params));
485 } else if ( op === 'taskRemove' ) {
486 self.removeItem(arg);
487 }
488 });
489 return self;
490 }
491 }, {
492 '@meta': {
493 'index': 5,
494 'group': true
495 },
496 '?': {
497 '@class': models.Task,
498 '@factory': TaskFactory
499 }
500 })
501 }
502 424
503 }); 425 function TaskFactory(json, parameters) {
426 var type = json.type || 'action';
427 var baseClass = taskTypes[parameters.wfType];
428 var mixinClass = taskTypes[type];
429 var taskClass = mixinClass.call(baseClass);
430 return taskClass.create(json, parameters);
431 }
504 432
505 models.ReverseWorkflow = models.Workflow.extend({}); 433 models.Workflow = fields.frozendict.extend({
506 models.DirectWorkflow = models.Workflow.extend({}, { 434 create: function(json, parameters) {
507 'task-defaults': { 435 var self = fields.frozendict.create.call(this, json, parameters);
508 '@class': fields.frozendict.extend({}, { 436 self.on('childChange', function(child, op) {
509 '@required': false, 437 if ( child === self.get('type') && op !== 'id' ) {
510 '@meta': { 438 self.emit('change', 'workflowType');
511 'index': 4,
512 'group': true,
513 'additive': false
514 },
515 'on-error': {
516 '@class': models.yaqllist.extend({}, {
517 '@meta': {
518 'title': 'On error',
519 'index': 0
520 }
521 })
522 },
523 'on-success': {
524 '@class': models.yaqllist.extend({}, {
525 '@meta': {
526 'title': 'On success',
527 'index': 1
528 }
529 })
530 },
531 'on-complete': {
532 '@class': models.yaqllist.extend({}, {
533 '@meta': {
534 'title': 'On complete',
535 'index': 2
536 }
537 })
538 }
539 })
540 } 439 }
541 }); 440 });
542 441 return self;
543 var workflowTypes = { 442 }
544 'direct': models.DirectWorkflow, 443 }, {
545 'reverse': models.ReverseWorkflow 444 'type': {
546 }; 445 '@class': fields.string.extend({}, {
547 446 '@enum': ['reverse', 'direct'],
548 function workflowFactory(json, parameters) { 447 '@default': 'direct',
549 var type = json.type || 'direct'; 448 '@meta': {
550 parameters.wfType = type; 449 'index': 1,
551 return workflowTypes[type].create(json, parameters); 450 'row': 0
552 } 451 }
553 452 })
554 models.Actions = fields.dictionary.extend({}, { 453 },
454 'input': {
455 '@class': fields.list.extend({}, {
555 '@required': false, 456 '@required': false,
556 '@meta': { 457 '@meta': {
557 'index': 3, 458 'index': 2
558 'panelIndex': 1
559 }, 459 },
560 '?': { 460 '*': {
561 '@class': models.Action 461 '@class': fields.string
562 } 462 }
563 }); 463 })
564 464 },
565 models.Workflows = fields.dictionary.extend({ 465 'output': {
466 '@class': fields.list.extend({}, {
467 '@required': false,
468 '@meta': {
469 'index': 3
470 },
471 '*': {
472 '@class': fields.string
473 }
474 })
475 },
476 'tasks': {
477 '@class': fields.dictionary.extend({
566 create: function(json, parameters) { 478 create: function(json, parameters) {
567 var self = fields.dictionary.create.call(this, json, parameters); 479 var self = fields.dictionary.create.call(this, json, parameters);
568 self.on('childChange', function(child, op) { 480 self.on('childChange', function(child, op, arg) {
569 if ( op === 'workflowType' ) { 481 if ( op === 'taskType' ) {
570 var workflowId = child.getID(), 482 var taskId = child.getID();
571 workflowPos = self.getPosByID(workflowId), 483 var params = child._parameters;
572 params = child._parameters, 484 var taskPos = self.getPosByID(taskId);
573 workflowData = child.toJSON(); 485 var taskData = child.toJSON();
574 params.wfType = child.type; 486 params.id = taskId;
575 params.id = workflowId; 487 self.set(taskPos, TaskFactory(taskData, params));
576 self.set(workflowPos, workflowFactory(workflowData, params)); 488 } else if ( op === 'taskRemove' ) {
489 self.removeItem(arg);
577 } 490 }
578 }); 491 });
579 return self; 492 return self;
580 } 493 }
581 }, { 494 }, {
582 '@meta': { 495 '@meta': {
583 'index': 4, 496 'index': 5,
584 'panelIndex': 2 497 'group': true
585 }, 498 },
586 '?': { 499 '?': {
587 '@class': models.Workflow, 500 '@class': models.Task,
588 '@factory': workflowFactory 501 '@factory': TaskFactory
589 } 502 }
590 }); 503 })
504 }
591 505
592 models.Workbook = fields.frozendict.extend({ 506 });
593 toYAML: function() { 507
594 return jsyaml.dump(this.toJSON({pretty: true})); 508 models.ReverseWorkflow = models.Workflow.extend({});
595 } 509 models.DirectWorkflow = models.Workflow.extend({}, {
596 }, { 510 'task-defaults': {
597 'version': { 511 '@class': fields.frozendict.extend({}, {
598 '@class': fields.string.extend({}, { 512 '@required': false,
599 '@enum': ['2.0'], 513 '@meta': {
514 'index': 4,
515 'group': true,
516 'additive': false
517 },
518 'on-error': {
519 '@class': models.yaqllist.extend({}, {
600 '@meta': { 520 '@meta': {
601 'index': 2, 521 'title': 'On error',
602 'panelIndex': 0, 522 'index': 0
603 'row': 1 523 }
604 },
605 '@default': '2.0'
606 }) 524 })
607 }, 525 },
608 'name': { 526 'on-success': {
609 '@class': fields.string.extend({}, { 527 '@class': models.yaqllist.extend({}, {
610 '@meta': { 528 '@meta': {
611 'index': 0, 529 'title': 'On success',
612 'panelIndex': 0, 530 'index': 1
613 'row': 0 531 }
614 },
615 '@constraints': [
616 function(value) {
617 return value !== 'workbook1' ? true : 'The sample validation failure.';
618 }
619 ]
620 }) 532 })
621 }, 533 },
622 'description': { 534 'on-complete': {
623 '@class': fields.text.extend({}, { 535 '@class': models.yaqllist.extend({}, {
624 '@meta': { 536 '@meta': {
625 'index': 1, 537 'title': 'On complete',
626 'panelIndex': 0, 538 'index': 2
627 'row': 0 539 }
628 },
629 '@required': false
630 }) 540 })
631 },
632 'actions': {
633 '@class': models.Actions
634 },
635 'workflows': {
636 '@class': models.Workflows
637 } 541 }
638 }); 542 })
543 }
544 });
639 545
640 models.StandardActions = Barricade.create({ 546 var workflowTypes = {
641 '@type': Object, 547 'direct': models.DirectWorkflow,
642 '?': { 548 'reverse': models.ReverseWorkflow
643 '@type': Array, 549 };
644 '*': { 550
645 '@type': String 551 function workflowFactory(json, parameters) {
646 } 552 var type = json.type || 'direct';
553 parameters.wfType = type;
554 return workflowTypes[type].create(json, parameters);
555 }
556
557 models.Actions = fields.dictionary.extend({}, {
558 '@required': false,
559 '@meta': {
560 'index': 3,
561 'panelIndex': 1
562 },
563 '?': {
564 '@class': models.Action
565 }
566 });
567
568 models.Workflows = fields.dictionary.extend({
569 create: function(json, parameters) {
570 var self = fields.dictionary.create.call(this, json, parameters);
571 self.on('childChange', function(child, op) {
572 if ( op === 'workflowType' ) {
573 var workflowId = child.getID();
574 var workflowPos = self.getPosByID(workflowId);
575 var params = child._parameters;
576 var workflowData = child.toJSON();
577 params.wfType = child.type;
578 params.id = workflowId;
579 self.set(workflowPos, workflowFactory(workflowData, params));
647 } 580 }
648 }); 581 });
582 return self;
583 }
584 }, {
585 '@meta': {
586 'index': 4,
587 'panelIndex': 2
588 },
589 '?': {
590 '@class': models.Workflow,
591 '@factory': workflowFactory
592 }
593 });
649 594
650 models.Root = Barricade.ImmutableObject.extend({}, { 595 models.Workbook = fields.frozendict.extend({
651 '@type': Object, 596 toYAML: function() {
652 'standardActions': { 597 return jsyaml.dump(this.toJSON({pretty: true}));
653 '@class': models.StandardActions 598 }
599 }, {
600 'version': {
601 '@class': fields.string.extend({}, {
602 '@enum': ['2.0'],
603 '@meta': {
604 'index': 2,
605 'panelIndex': 0,
606 'row': 1
654 }, 607 },
655 'workbook': { 608 '@default': '2.0'
656 '@class': models.Workbook 609 })
657 } 610 },
658 }); 611 'name': {
612 '@class': fields.string.extend({}, {
613 '@meta': {
614 'index': 0,
615 'panelIndex': 0,
616 'row': 0
617 },
618 '@constraints': [
619 function(value) {
620 return value !== 'workbook1' ? true : 'The sample validation failure.';
621 }
622 ]
623 })
624 },
625 'description': {
626 '@class': fields.text.extend({}, {
627 '@meta': {
628 'index': 1,
629 'panelIndex': 0,
630 'row': 0
631 },
632 '@required': false
633 })
634 },
635 'actions': {
636 '@class': models.Actions
637 },
638 'workflows': {
639 '@class': models.Workflows
640 }
641 });
642
643 models.StandardActions = Barricade.create({
644 '@type': Object,
645 '?': {
646 '@type': Array,
647 '*': {
648 '@type': String
649 }
650 }
651 });
652
653 models.Root = Barricade.ImmutableObject.extend({}, {
654 '@type': Object,
655 'standardActions': {
656 '@class': models.StandardActions
657 },
658 'workbook': {
659 '@class': models.Workbook
660 }
661 });
659 662
660 return models; 663 return models;
661 }]) 664 }
662})(); 665})();
diff --git a/extensions/mistral/templates/mistral/create.html b/extensions/mistral/templates/mistral/create.html
index 430d208..27be743 100644
--- a/extensions/mistral/templates/mistral/create.html
+++ b/extensions/mistral/templates/mistral/create.html
@@ -38,34 +38,36 @@
38 38
39{% block main %} 39{% block main %}
40<h3>Create Workbook</h3> 40<h3>Create Workbook</h3>
41 <div id="create-workbook" class="fluid-container" ng-cloak ng-controller="workbookCtrl" 41 <div id="create-workbook" class="fluid-container" ng-cloak ng-controller="WorkbookController as wb"
42 ng-init="init({{ id|default:'undefined' }}, '{{ yaml }}', '{{ commit_url }}', '{{ discard_url }}')"> 42 ng-init="wb.init({{ id|default:'undefined' }}, '{{ yaml }}', '{{ commit_url }}', '{{ discard_url }}')">
43 <div class="well"> 43 <div class="well">
44 <div class="two-panels"> 44 <div class="two-panels">
45 <div class="left-panel"> 45 <div class="left-panel">
46 <div class="pull-left"> 46 <div class="pull-left">
47 <h4><strong>{$ workbook.get('name') $}</strong></h4> 47 <h4><strong>{$ wb.workbook.get('name') $}</strong></h4>
48 </div> 48 </div>
49 <div class="pull-right"> 49 <div class="pull-right">
50 <div class="table-actions clearfix"> 50 <div class="table-actions clearfix">
51 <button ng-click="addAction()" class="btn btn-default btn-sm"><span class="fa fa-plus">Add Action</span></button> 51 <button ng-click="wb.addAction()" class="btn btn-default btn-sm">
52 <button ng-click="addWorkflow()" class="btn btn-default btn-sm"><span class="fa fa-plus">Add Workflow</span></button> 52 <span class="fa fa-plus">Add Action</span></button>
53 <button ng-click="wb.addWorkflow()" class="btn btn-default btn-sm">
54 <span class="fa fa-plus">Add Workflow</span></button>
53 </div> 55 </div>
54 </div> 56 </div>
55 </div> 57 </div>
56 <div class="right-panel"> 58 <div class="right-panel">
57 <div class="btn-group btn-toggle pull-right"> 59 <div class="btn-group btn-toggle pull-right">
58 <button ng-click="isGraphMode = true" class="btn btn-sm" 60 <button ng-click="wb.isGraphMode = true" class="btn btn-sm"
59 ng-class="isGraphMode ? 'active btn-primary' : 'btn-default'">Graph</button> 61 ng-class="wb.isGraphMode ? 'active btn-primary' : 'btn-default'">Graph</button>
60 <button ng-click="isGraphMode = false" class="btn btn-sm" 62 <button ng-click="wb.isGraphMode = false" class="btn btn-sm"
61 ng-class="!isGraphMode ? 'active btn-primary' : 'btn-default'">YAML</button> 63 ng-class="!wb.isGraphMode ? 'active btn-primary' : 'btn-default'">YAML</button>
62 </div> 64 </div>
63 </div> 65 </div>
64 </div> 66 </div>
65 <!-- Data panel start --> 67 <!-- Data panel start -->
66 <div class="two-panels"> 68 <div class="two-panels">
67 <div class="left-panel"> 69 <div class="left-panel">
68 <panel ng-repeat="panel in workbook | extractPanels track by panel.id" 70 <panel ng-repeat="panel in wb.workbook | extractPanels track by panel.id"
69 content="panel"> 71 content="panel">
70 <div ng-repeat="row in panel | extractRows track by row.id"> 72 <div ng-repeat="row in panel | extractRows track by row.id">
71 <div ng-class="{'two-columns': row.index !== undefined }"> 73 <div ng-class="{'two-columns': row.index !== undefined }">
@@ -81,10 +83,10 @@
81 <!-- YAML Panel --> 83 <!-- YAML Panel -->
82 <div class="right-panel"> 84 <div class="right-panel">
83 <div class="panel panel-default"> 85 <div class="panel panel-default">
84 <div class="panel-body" ng-show="!isGraphMode"> 86 <div class="panel-body" ng-show="!wb.isGraphMode">
85 <pre>{$ workbook.toYAML() $}</pre> 87 <pre>{$ wb.workbook.toYAML() $}</pre>
86 </div> 88 </div>
87 <div class="panel-body" ng-show="isGraphMode"> 89 <div class="panel-body" ng-show="wb.isGraphMode">
88 Here will be a fancy Graph View as soon as we implement it! 90 Here will be a fancy Graph View as soon as we implement it!
89 </div> 91 </div>
90 </div> 92 </div>
@@ -94,9 +96,9 @@
94 <div class="two-panels"> 96 <div class="two-panels">
95 <div class="full-width"> 97 <div class="full-width">
96 <div class="pull-right"> 98 <div class="pull-right">
97 <button ng-click="discardWorkbook()" class="btn btn-default cancel">Cancel</button> 99 <button ng-click="wb.discardWorkbook()" class="btn btn-default cancel">Cancel</button>
98 <button ng-click="commitWorkbook()" class="btn btn-primary"> 100 <button ng-click="wb.commitWorkbook()" class="btn btn-primary">
99 {$ workbookID ? 'Modify' : 'Create' $} 101 {$ wb.workbookID ? 'Modify' : 'Create' $}
100 </button> 102 </button>
101 </div> 103 </div>
102 </div> 104 </div>
diff --git a/extensions/mistral/test/js/workbook.controller.spec.js b/extensions/mistral/test/js/workbook.controller.spec.js
index 92317fb..c2ce5ee 100644
--- a/extensions/mistral/test/js/workbook.controller.spec.js
+++ b/extensions/mistral/test/js/workbook.controller.spec.js
@@ -27,24 +27,22 @@ describe('together workbook model and controller', function() {
27 27
28 28
29 describe('define top-level actions available to user:', function () { 29 describe('define top-level actions available to user:', function () {
30 var $scope; 30 var wbCtrl;
31 31
32 beforeEach(inject(function (_$controller_) { 32 beforeEach(inject(function (_$controller_) {
33 var $controller = _$controller_; 33 wbCtrl = _$controller_('WorkbookController', {});
34 $scope = {}; 34 wbCtrl.workbook = workbook;
35 $controller('workbookCtrl', {$scope: $scope});
36 $scope.workbook = workbook;
37 })); 35 }));
38 36
39 describe("'Add Action' action", function () { 37 describe("'Add Action' action", function () {
40 it('adds a new Action', function () { 38 it('adds a new Action', function () {
41 $scope.addAction(); 39 wbCtrl.addAction();
42 40
43 expect(workbook.get('actions').get(0)).toBeDefined(); 41 expect(workbook.get('actions').get(0)).toBeDefined();
44 }); 42 });
45 43
46 it('creates action with predefined name', function () { 44 it('creates action with predefined name', function () {
47 $scope.addAction(); 45 wbCtrl.addAction();
48 46
49 expect(workbook.get('actions').get(0).getID()).toBeGreaterThan(''); 47 expect(workbook.get('actions').get(0).getID()).toBeGreaterThan('');
50 }); 48 });
@@ -56,7 +54,7 @@ describe('together workbook model and controller', function() {
56 })); 54 }));
57 55
58 it("corresponding JSON has the right key for the Action", function () { 56 it("corresponding JSON has the right key for the Action", function () {
59 $scope.addAction(); 57 wbCtrl.addAction();
60 58
61 expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeDefined(); 59 expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeDefined();
62 }); 60 });
@@ -64,7 +62,7 @@ describe('together workbook model and controller', function() {
64 it("once the Action ID is changed, it's reflected in JSON", function () { 62 it("once the Action ID is changed, it's reflected in JSON", function () {
65 var newID = 'action10'; 63 var newID = 'action10';
66 64
67 $scope.addAction(); 65 wbCtrl.addAction();
68 workbook.get('actions').getByID(actionID).setID(newID); 66 workbook.get('actions').getByID(actionID).setID(newID);
69 67
70 expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeUndefined(); 68 expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeUndefined();
@@ -74,8 +72,8 @@ describe('together workbook model and controller', function() {
74 }); 72 });
75 73
76 it('creates actions with different names on 2 successive calls', function () { 74 it('creates actions with different names on 2 successive calls', function () {
77 $scope.addAction(); 75 wbCtrl.addAction();
78 $scope.addAction(); 76 wbCtrl.addAction();
79 77
80 expect(workbook.get('actions').get(0).getID()).not.toEqual( 78 expect(workbook.get('actions').get(0).getID()).not.toEqual(
81 workbook.get('actions').get(1).getID()) 79 workbook.get('actions').get(1).getID())
@@ -84,7 +82,7 @@ describe('together workbook model and controller', function() {
84 82
85 describe("'Add Workflow' action", function () { 83 describe("'Add Workflow' action", function () {
86 it('adds a new Workflow', function () { 84 it('adds a new Workflow', function () {
87 $scope.addWorkflow(); 85 wbCtrl.addWorkflow();
88 86
89 expect(workbook.get('workflows').get(0)).toBeDefined(); 87 expect(workbook.get('workflows').get(0)).toBeDefined();
90 }); 88 });
@@ -96,7 +94,7 @@ describe('together workbook model and controller', function() {
96 })); 94 }));
97 95
98 it("corresponding JSON has the right key for the Workflow", function () { 96 it("corresponding JSON has the right key for the Workflow", function () {
99 $scope.addWorkflow(); 97 wbCtrl.addWorkflow();
100 98
101 expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeDefined(); 99 expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeDefined();
102 }); 100 });
@@ -104,7 +102,7 @@ describe('together workbook model and controller', function() {
104 it("once the workflow ID is changed, it's reflected in JSON", function () { 102 it("once the workflow ID is changed, it's reflected in JSON", function () {
105 var newID = 'workflow10'; 103 var newID = 'workflow10';
106 104
107 $scope.addWorkflow(); 105 wbCtrl.addWorkflow();
108 workbook.get('workflows').getByID(workflowID).setID(newID); 106 workbook.get('workflows').getByID(workflowID).setID(newID);
109 107
110 expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeUndefined(); 108 expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeUndefined();
@@ -114,14 +112,14 @@ describe('together workbook model and controller', function() {
114 }); 112 });
115 113
116 it('creates workflow with predefined name', function () { 114 it('creates workflow with predefined name', function () {
117 $scope.addWorkflow(); 115 wbCtrl.addWorkflow();
118 116
119 expect(workbook.get('workflows').get(0).getID()).toBeGreaterThan(''); 117 expect(workbook.get('workflows').get(0).getID()).toBeGreaterThan('');
120 }); 118 });
121 119
122 it('creates workflows with different names on 2 successive calls', function () { 120 it('creates workflows with different names on 2 successive calls', function () {
123 $scope.addWorkflow(); 121 wbCtrl.addWorkflow();
124 $scope.addWorkflow(); 122 wbCtrl.addWorkflow();
125 123
126 expect(workbook.get('workflows').get(0).getID()).not.toEqual( 124 expect(workbook.get('workflows').get(0).getID()).not.toEqual(
127 workbook.get('workflows').get(1).getID()) 125 workbook.get('workflows').get(1).getID())
diff --git a/package.json b/package.json
index 6156fe9..ebb4dcb 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,6 @@
43 "postinstall": "bower install", 43 "postinstall": "bower install",
44 "test-unit": "grunt test:unit", 44 "test-unit": "grunt test:unit",
45 "test": "karma start ./karma-unit.conf.js", 45 "test": "karma start ./karma-unit.conf.js",
46 "lint": "eslint --no-color ./merlin/static" 46 "lint": "eslint --no-color ./merlin/static ./extensions/mistral/static"
47 } 47 }
48} 48}