Move Mistral schemas to a separate file

Enable work with 2 types of Task: Action-based and Workflow-based
ones. Move to refactored Barricade.js lib (reference resolving was
greatly simplified, pleasure to work with it now!).

Change-Id: I15173a5f3bbfc22b6d19b8fece150bb70fc39a4e
This commit is contained in:
Timur Sufiev 2014-09-26 20:33:17 +04:00
parent caf3d9e995
commit af984c18d3
4 changed files with 530 additions and 580 deletions

View File

@ -5,6 +5,7 @@
<title>Merlin Project</title> <title>Merlin Project</title>
<script src="js/lib/jquery-1.11.1.js"></script> <script src="js/lib/jquery-1.11.1.js"></script>
<script src="js/lib/barricade.js"></script> <script src="js/lib/barricade.js"></script>
<script src="js/schema.js"></script>
<script src="js/merlin.js"></script> <script src="js/merlin.js"></script>
<script src="js/lib/js-yaml.js"></script> <script src="js/lib/js-yaml.js"></script>
<link rel="stylesheet" href="css/merlin.css"> <link rel="stylesheet" href="css/merlin.css">

View File

@ -26,13 +26,109 @@ Barricade = (function () {
}); });
} }
f.apply(this, arguments); return f.apply(this, arguments);
}; };
return g; return g;
} }
}; };
var Extendable = Blueprint.create(function () {
function forInKeys(obj) {
var key,
keys = [];
for (key in obj) {
keys.push(key);
}
return keys;
}
function isPlainObject(obj) {
return getType(obj) === Object &&
Object.getPrototypeOf(Object.getPrototypeOf(obj)) === null;
}
function extend(extension) {
function addProperty(object, prop) {
return Object.defineProperty(object, prop, {
enumerable: true,
writable: true,
configurable: true,
value: extension[prop]
});
}
// add properties to extended object
return Object.keys(extension).reduce(addProperty,
Object.create(this));
}
function deepClone(object) {
if (isPlainObject(object)) {
return forInKeys(object).reduce(function (clone, key) {
clone[key] = deepClone(object[key]);
return clone;
}, {});
}
return object;
}
function merge(target, source) {
forInKeys(source).forEach(function (key) {
if (target.hasOwnProperty(key) &&
isPlainObject(target[key]) &&
isPlainObject(source[key])) {
merge(target[key], source[key]);
} else {
target[key] = deepClone(source[key]);
}
});
}
return Object.defineProperty(this, 'extend', {
enumerable: false,
writable: false,
value: function (extension, schema) {
if (schema) {
extension._schema = '_schema' in this ?
deepClone(this._schema) : {};
merge(extension._schema, schema);
}
return extend.call(this, extension);
}
});
});
var InstanceofMixin = Blueprint.create(function () {
return Object.defineProperty(this, 'instanceof', {
enumerable: false,
value: function (proto) {
var _instanceof = this.instanceof,
subject = this;
function hasMixin(obj, mixin) {
return obj.hasOwnProperty('_parents') &&
obj._parents.some(function (_parent) {
return _instanceof.call(_parent, mixin);
});
}
do {
if (subject === proto ||
hasMixin(subject, proto)) {
return true;
}
subject = Object.getPrototypeOf(subject);
} while (subject);
return false;
}
});
});
var Identifiable = Blueprint.create(function (id) { var Identifiable = Blueprint.create(function (id) {
this.getID = function () { this.getID = function () {
return id; return id;
@ -45,12 +141,6 @@ Barricade = (function () {
}); });
var Omittable = Blueprint.create(function (isUsed) { var Omittable = Blueprint.create(function (isUsed) {
var defaultValue = this._schema['@default'];
if ( defaultValue ) {
this._setData(defaultValue);
}
this.isUsed = function () { this.isUsed = function () {
// If required, it has to be used. // If required, it has to be used.
return this.isRequired() || isUsed; return this.isRequired() || isUsed;
@ -78,20 +168,31 @@ Barricade = (function () {
return ref; return ref;
} }
function hasDependency() { if (schema.hasOwnProperty('@ref')) {
return schema.hasOwnProperty('@ref'); deferred = Deferred.create(schema['@ref'].needs, resolver);
} }
this.hasDependency = hasDependency; this.resolveWith = function (obj) {
var allResolved = true;
if (hasDependency()) { if (deferred && !deferred.isResolved()) {
this.getDeferred = function () { if (deferred.needs(obj)) {
return deferred; this.emit('replace', deferred.resolve(obj));
}; } else {
allResolved = false;
}
}
deferred = Deferred.create(schema['@ref'].needs, if (this.instanceof(Container)) {
resolver); this.each(function (index, value) {
} if (!value.resolveWith(obj)) {
allResolved = false;
}
});
}
return allResolved;
};
}); });
var Validatable = Blueprint.create(function (schema) { var Validatable = Blueprint.create(function (schema) {
@ -196,213 +297,111 @@ Barricade = (function () {
var Deferred = { var Deferred = {
create: function (classGetter, onResolve) { create: function (classGetter, onResolve) {
var self = Object.create(this), var self = Object.create(this);
callbacks = [], self._isResolved = false;
isResolved = false; self._classGetter = classGetter;
self._onResolve = onResolve;
self.getClass = function () {
return classGetter();
};
self.resolve = function (obj) {
var ref;
if (isResolved) {
throw new Error('Deferred already resolved');
}
ref = onResolve(obj);
if (ref === undefined) {
logError('Could not resolve reference');
} else {
isResolved = true;
callbacks.forEach(function (callback) {
callback(ref);
});
}
return ref;
};
self.isResolved = function () {
return isResolved;
};
self.addCallback = function (callback) {
callbacks.push(callback);
};
return self; return self;
},
resolve: function (obj) {
var ref;
if (this._isResolved) {
throw new Error('Deferred already resolved');
}
ref = this._onResolve(obj);
if (ref !== undefined) {
this._isResolved = true;
return ref;
}
},
isResolved: function () {
return this._isResolved;
},
needs: function (obj) {
return obj.instanceof(this._classGetter());
} }
}; };
var Base = (function () { var Base = Extendable.call(InstanceofMixin.call({
var base = {}; create: function (json, parameters) {
var self = this.extend({}),
schema = self._schema,
type = schema['@type'],
isUsed;
function forInKeys(obj) { self._parameters = parameters = parameters || {};
var key,
keys = [];
for (key in obj) { if (schema.hasOwnProperty('@inputMassager')) {
keys.push(key); json = schema['@inputMassager'](json);
} }
return keys; isUsed = self._setData(json);
}
function isPlainObject(obj) { if (schema.hasOwnProperty('@toJSON')) {
return getType(obj) === Object && self.toJSON = schema['@toJSON'];
Object.getPrototypeOf(Object.getPrototypeOf(obj)) === null;
}
function extend(extension) {
function addProperty(object, prop) {
return Object.defineProperty(object, prop, {
enumerable: true,
writable: true,
configurable: true,
value: extension[prop]
});
} }
// add properties to extended object Observable.call(self);
return Object.keys(extension).reduce(addProperty, Omittable.call(self, isUsed);
Object.create(this)); Deferrable.call(self, schema);
} Validatable.call(self, schema);
function deepClone(object) { if (schema.hasOwnProperty('@enum')) {
if (isPlainObject(object)) { Enumerated.call(self, schema['@enum']);
return forInKeys(object).reduce(function (clone, key) {
clone[key] = deepClone(object[key]);
return clone;
}, {});
} }
return object;
}
function merge(target, source) { if (parameters.hasOwnProperty('id')) {
forInKeys(source).forEach(function (key) { Identifiable.call(self, parameters.id);
if (target.hasOwnProperty(key) && }
isPlainObject(target[key]) &&
isPlainObject(source[key])) { return self;
merge(target[key], source[key]); },
_setData: function(json) {
var isUsed = true,
type = this._schema['@type'];
if (getType(json) !== type) {
if (json) {
logError("Type mismatch (json, schema)");
logVal(json, this._schema);
} else { } else {
target[key] = deepClone(source[key]); isUsed = false;
} }
}); // Replace bad type (does not change original)
json = this._getDefaultValue();
}
this._data = this._sift(json, this._parameters);
return isUsed;
},
_getDefaultValue: function () {
return this._schema.hasOwnProperty('@default')
? typeof this._schema['@default'] === 'function'
? this._schema['@default']()
: this._schema['@default']
: this._schema['@type']();
},
_sift: function () {
throw new Error("sift() must be overridden in subclass");
},
_safeInstanceof: function (instance, class_) {
return typeof instance === 'object' &&
('instanceof' in instance) &&
instance.instanceof(class_);
},
getPrimitiveType: function () {
return this._schema['@type'];
},
isRequired: function () {
return this._schema['@required'] !== false;
},
isEmpty: function () {
throw new Error('Subclass should override isEmpty()');
} }
}));
Object.defineProperty(base, 'extend', {
enumerable: false,
writable: false,
value: function (extension, schema) {
if (schema) {
extension._schema = '_schema' in this ?
deepClone(this._schema) : {};
merge(extension._schema, schema);
}
return extend.call(this, extension);
}
});
Object.defineProperty(base, 'instanceof', {
enumerable: false,
value: function (proto) {
var _instanceof = this.instanceof,
subject = this;
function hasMixin(obj, mixin) {
return obj.hasOwnProperty('_parents') &&
obj._parents.some(function (_parent) {
return _instanceof.call(_parent, mixin);
});
}
do {
if (subject === proto ||
hasMixin(subject, proto)) {
return true;
}
subject = Object.getPrototypeOf(subject);
} while (subject);
return false;
}
});
return base.extend({
create: function (json, parameters) {
var self = this.extend({}),
schema = self._schema,
type = schema['@type'],
isUsed;
self._parameters = parameters = parameters || {};
if (schema.hasOwnProperty('@inputMassager')) {
json = schema['@inputMassager'](json);
}
isUsed = self._setData(json);
if (schema.hasOwnProperty('@toJSON')) {
self.toJSON = schema['@toJSON'];
}
Observable.call(self);
Omittable.call(self, isUsed);
Deferrable.call(self, schema);
Validatable.call(self, schema);
if (schema.hasOwnProperty('@enum')) {
Enumerated.call(self, schema['@enum']);
}
if (parameters.hasOwnProperty('id')) {
Identifiable.call(self, parameters.id);
}
return self;
},
_setData: function(json) {
var isUsed = true,
type = this._schema['@type'];
if (getType(json) !== type) {
if (json) {
logError("Type mismatch (json, schema)");
logVal(json, this._schema);
} else {
isUsed = false;
}
// Replace bad type (does not change original)
json = type();
}
this._data = this._sift(json, this._parameters);
return isUsed;
},
_sift: function () {
throw new Error("sift() must be overridden in subclass");
},
_safeInstanceof: function (instance, class_) {
return typeof instance === 'object' &&
('instanceof' in instance) &&
instance.instanceof(class_);
},
getPrimitiveType: function () {
return this._schema['@type'];
},
isRequired: function () {
return this._schema['@required'] !== false;
},
isEmpty: function () {
throw new Error('Subclass should override isEmpty()');
}
});
}());
var Container = Base.extend({ var Container = Base.extend({
create: function (json, parameters) { create: function (json, parameters) {
@ -413,173 +412,54 @@ Barricade = (function () {
self._attachListeners(key); self._attachListeners(key);
} }
function getOnResolve(key) { self.on('_addedElement', function (key) {
return function (resolvedValue) { attachListeners(key);
self.set(key, resolvedValue); self._tryResolveOn(self.get(key));
});
if (resolvedValue.hasDependency()) {
allDeferred.push(resolvedValue.getDeferred());
}
if ('getAllDeferred' in resolvedValue) {
allDeferred = allDeferred.concat(
resolvedValue.getAllDeferred());
}
};
}
function attachDeferredCallback(key, value) {
if (value.hasDependency()) {
value.getDeferred().addCallback(getOnResolve(key));
}
}
function deferredClassMatches(deferred) {
return self.instanceof(deferred.getClass());
}
function addDeferredToList(obj) {
if (obj.hasDependency()) {
allDeferred.push(obj.getDeferred());
}
if ('getAllDeferred' in obj) {
allDeferred = allDeferred.concat(
obj.getAllDeferred());
}
}
function resolveDeferreds() {
var curDeferred,
unresolvedDeferreds = [];
// New deferreds can be added to allDeferred as others are
// resolved. Iterating this way is safe regardless of how
// new elements are added.
while (allDeferred.length > 0) {
curDeferred = allDeferred.shift();
if (!curDeferred.isResolved()) {
if (deferredClassMatches(curDeferred)) {
curDeferred.addCallback(addDeferredToList);
curDeferred.resolve(self);
} else {
unresolvedDeferreds.push(curDeferred);
}
}
}
allDeferred = unresolvedDeferreds;
}
self.on('_addedElement', attachListeners);
self.each(attachListeners); self.each(attachListeners);
self.on('_addedElement', function(index) { self.each(function (index, value) {
var element = self.get(index); value.resolveWith(self);
attachDeferredCallback(index, element);
addDeferredToList(element);
resolveDeferreds.call(self);
if ( (element.instanceof(Barricade.Container) &&
element.getAllDeferred().length ) ||
(element.instanceof(Barricade.Primitive) &&
element.hasDependency() &&
!element.getDeferred().isResolved()) ) {
element.emit('_resolveUp');
}
}); });
function mergeBubblingDeferreds(deferreds) {
var currentIds = {},
bubblingDeferreds = {},
id;
if ( deferreds && deferreds.length ) {
allDeferred.forEach(function(deferred) {
if (!(deferred.id in currentIds)) {
currentIds[deferred.id] = true;
}
});
deferreds.forEach(function(deferred) {
if (!(deferred.id in bubblingDeferreds)) {
bubblingDeferreds[deferred.id] = deferred;
}
});
for ( id in bubblingDeferreds ) {
if (!(id in currentIds)) {
allDeferred.push(bubblingDeferreds[id]);
}
}
}
}
self.on('_resolveUp', function(deferreds) {
mergeBubblingDeferreds(deferreds);
resolveDeferreds.call(self);
});
self.each(function (key, value) {
attachDeferredCallback(key, value);
});
if (self.hasDependency()) {
allDeferred.push(self.getDeferred());
}
self.each(function (key, value) {
addDeferredToList(value);
});
resolveDeferreds.call(self);
self.getAllDeferred = function () {
return allDeferred;
};
return self; return self;
}, },
_tryResolveOn: function (value) {
if (!value.resolveWith(this)) {
this.emit('_resolveUp', value);
}
},
_attachListeners: function (key) { _attachListeners: function (key) {
var self = this, var self = this,
element = this.get(key); element = this.get(key),
events = {
function onChildChange(child) { 'childChange': function (child) {
self.emit('childChange', child); self.emit('childChange', child);
} },
'change': function () {
function onDirectChildChange() { // 'this' is set to callee, no typo
onChildChange(this); // 'this' is set to callee, not typo events.childChange(this);
} },
'replace': function (newValue) {
function onReplace(newValue) { self.set(key, newValue);
self.set(key, newValue); self._tryResolveOn(newValue);
} },
'_resolveUp': function (value) {
element.on('_resolveUp', function() { self._tryResolveOn(value);
if ( element.instanceof(Barricade.Container) ) { },
self.emit('_resolveUp', element.getAllDeferred()); 'removeFrom': function (container) {
} else { if (container === self) {
self.emit('_resolveUp', [element.getDeferred()]); Object.keys(events).forEach(function (eName) {
} element.on(eName, events[eName]);
});
}
}
};
Object.keys(events).forEach(function (eName) {
element.on(eName, events[eName]);
}); });
element.on('childChange', onChildChange);
element.on('change', onDirectChildChange);
element.on('replace', onReplace);
element.on('removeFrom', function (container) {
if (container === self) {
element.off('childChange', onChildChange);
element.off('change', onDirectChildChange);
element.off('replace', onReplace);
}
});
},
set: function (key, value) {
this.get(key).emit('removeFrom', this);
this._doSet(key, value);
this._attachListeners(key);
}, },
_getKeyClass: function (key) { _getKeyClass: function (key) {
return this._schema[key].hasOwnProperty('@class') return this._schema[key].hasOwnProperty('@class')
@ -607,6 +487,11 @@ Barricade = (function () {
return this._safeInstanceof(instance, class_) || return this._safeInstanceof(instance, class_) ||
(class_._schema.hasOwnProperty('@ref') && isRefTo()); (class_._schema.hasOwnProperty('@ref') && isRefTo());
},
set: function (key, value) {
this.get(key).emit('removeFrom', this);
this._doSet(key, value);
this._attachListeners(key);
} }
}); });

View File

@ -13,208 +13,6 @@
under the License. under the License.
*/ */
var types = {
Mistral: {
actions: {}
},
HOT: {}
};
types.Mistral.Version = Barricade.create({
'@type': Number,
'@default': 2
});
types.Mistral.Action = Barricade.create({
'@type': Object,
'version': {
'@type': Number,
'@ref': {
to: types.Mistral.Version,
needs: function () {
return types.Mistral.Workbook;
},
resolver: function(json, parentObj) {
return parentObj.get('version')
}
}
},
'name': {'@type': String},
'base': {
'@type': String,
'@enum': function() {
// TODO: obtain list of predefined actions from Mistral server-side
var predefinedActions = ['createInstance', 'terminateInstance'],
actions = workbook.get('actions'),
currentItemIndex = actions.length() - 1;
actions.each(function(index, actionItem) {
var name = actionItem.get('name');
if ( index < currentItemIndex && !name.isEmpty() ) {
predefinedActions = predefinedActions.concat(name.get())
}
});
return predefinedActions;
}
},
'base-parameters': {
'@type': Object,
'@required': false,
'?': {'@type': String}
}
});
types.Mistral.Policy = Barricade.create({
'@type': Object,
'wait-before': {
'@type': Number,
'@required': false
},
'wait-after': {
'@type': Number,
'@required': false
},
'retry': {
'@type': Object,
'@required': false,
'count': {'@type': Number},
'delay': {'@type': Number},
'break-on': {
'@type': String,
'@required': false
}
}
});
types.Mistral.Task = Barricade.create({
'@type': Object,
'version': {
'@type': Number,
'@ref': {
to: types.Mistral.Version,
needs: function () {
return types.Mistral.Workbook;
},
resolver: function(json, parentObj) {
return parentObj.get('version')
}
}
},
'name': {'@type': String},
'parameters': {
'@type': Object,
'@required': false,
'?': {'@type': String}
},
'publish': {
'@type': String,
'@required': false
},
'policies': {
'@class': types.Mistral.Policy,
'@required': false
},
'requires': { // array of Task-s existing in the same workflow
'@type': Array,
'@required': false,
'*': {'@type': String}
},
'on-complete': {
'@type': String,
'@required': false
},
'on-success': {
'@type': String,
'@required': false
},
'on-error': {
'@type': String,
'@required': false
}
});
types.Mistral.WorkflowTask = types.Mistral.Task.extend({},
{
'workflow': {
'@type': String,
'@required': false
}
});
types.Mistral.ActionTask = types.Mistral.Task.extend({},
{
'action': {
'@type': String,
'@required': false
}
});
types.Mistral.Workflow = Barricade.create({
'@type': Object,
'version': {
'@type': Number,
'@ref': {
to: types.Mistral.Version,
needs: function () {
return types.Mistral.Workbook;
},
resolver: function(json, parentObj) {
return parentObj.get('version')
}
}
},
'name': {'@type': String},
'type': {
'@type': String,
'@enum': ['reverse', 'direct'],
'@default': 'direct'
},
'parameters': {
'@type': Object,
'@required': false,
'?': {'@type': String}
},
'output': {
'@type': String,
'@required': false
},
'tasks': {
'@type': Object,
'?': {'@class': types.Mistral.Task}
}
});
types.Mistral.Workbook = Barricade.create({
'@type': Object,
'version': {
'@class': types.Mistral.Version
},
'description': {
'@type': String,
'@required': false
},
'actions': {
'@type': Array,
'@required': false,
'*': {
'@class': types.Mistral.Action
}
},
'workflows': {
'@type': Array,
'*': {
'@class': types.Mistral.Workflow
}
}
});
var workbook, var workbook,
counter = 0; counter = 0;
$(function() { $(function() {
@ -234,23 +32,25 @@ $(function() {
return $item; return $item;
} }
function drawSelectNode(label, item) { function drawSelectElement(labels, values, selected) {
var $item = $('<div></div>'), var $input = $('<select>');
$label = $('<label></label>').text(label),
$input = $('<select>'),
labels = item.getEnumLabels(),
values = item.getEnumValues(),
$set = $('<button>').text('Set');
$set.click(function() {
item.set($input.val());
});
values.forEach(function(value, index) { values.forEach(function(value, index) {
var $opt = $('<option></option>').val(value).text(labels[index]); var $opt = $('<option></option>').val(value).text(labels[index]);
$input.append($opt); $input.append($opt);
}); });
$input.val(item.get()); $input.val(selected);
return $input;
}
function drawSelectNode(label, item) {
var $item = $('<div></div>'),
$label = $('<label></label>').text(label),
$set = $('<button>').text('Set');
$set.click(function() {
item.set($input.val());
});
$item.append($label); $item.append($label);
$item.append($input); $item.append(drawSelectElement(item.getEnumLabels(), item.getEnumValues(), item.get()));
$item.append($set); $item.append($set);
return $item; return $item;
@ -322,10 +122,16 @@ $(function() {
$label = $('<label></label>').attr('id', labelId).text(label).toggleClass('expandable'), $label = $('<label></label>').attr('id', labelId).text(label).toggleClass('expandable'),
$keyName = $('<input>'), $keyName = $('<input>'),
$addAction = $('<button>').text('Add').attr('disabled', true), $addAction = $('<button>').text('Add').attr('disabled', true),
$container = $('<div></div>').attr('id', containerId).hide(); $container = $('<div></div>').attr('id', containerId).hide(),
$typeSelector;
counter++; counter++;
$item.append($label); $item.append($label);
$item.append($keyName); $item.append($keyName);
if ( item.instanceof(types.base.AcceptsMixin) ) {
var labels = item.getLabels();
$typeSelector = drawSelectElement(labels, labels, labels[0]);
$item.append($typeSelector);
}
$item.append($addAction); $item.append($addAction);
drawContainer($container, item); drawContainer($container, item);
$item.append($container); $item.append($container);
@ -337,8 +143,12 @@ $(function() {
} }
}); });
$addAction.click(function() { $addAction.click(function() {
var key = $keyName.val(); var key = $keyName.val(), child, cls;
item.push(undefined, {id: key}); if ( item.instanceof(types.base.AcceptsMixin) ) {
cls = item.getValue($typeSelector.val());
child = cls.create(undefined, {id: key});
}
item.push(child, {id: key});
drawTypedNode($container, key, item.getByID(key)); drawTypedNode($container, key, item.getByID(key));
}); });
$label.click(function() { $label.click(function() {

254
js/schema.js Normal file
View File

@ -0,0 +1,254 @@
/* Copyright (c) 2014 Mirantis, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
*/
var types = {
Mistral: {
actions: {}
},
base: {}
};
types.base.AcceptsMixin = Barricade.Blueprint.create(function (acceptsList) {
acceptsList = acceptsList || [];
this.getLabels = function() {
return acceptsList.map(function(item) {
return item.label;
})
};
this.getValue = function(label) {
for ( var i = 0; i < acceptsList.length; i++ ) {
if ( acceptsList[i].label === label ) {
return acceptsList[i].value;
}
}
return null;
}
});
types.Mistral.Version = Barricade.create({
'@type': Number,
'@default': 2
});
types.Mistral.Action = Barricade.create({
'@type': Object,
'version': {
'@type': Number,
'@ref': {
to: types.Mistral.Version,
needs: function () {
return types.Mistral.Workbook;
},
resolver: function(json, parentObj) {
return parentObj.get('version')
}
}
},
'name': {'@type': String},
'base': {
'@type': String,
'@enum': function() {
// TODO: obtain list of predefined actions from Mistral server-side
var predefinedActions = ['createInstance', 'terminateInstance'],
actions = workbook.get('actions'),
currentItemIndex = actions.length() - 1;
actions.each(function(index, actionItem) {
var name = actionItem.get('name');
if ( index < currentItemIndex && !name.isEmpty() ) {
predefinedActions = predefinedActions.concat(name.get())
}
});
return predefinedActions;
}
},
'base-parameters': {
'@type': Object,
'@required': false,
'?': {'@type': String}
}
});
types.Mistral.Policy = Barricade.create({
'@type': Object,
'wait-before': {
'@type': Number,
'@required': false
},
'wait-after': {
'@type': Number,
'@required': false
},
'retry': {
'@type': Object,
'@required': false,
'count': {'@type': Number},
'delay': {'@type': Number},
'break-on': {
'@type': String,
'@required': false
}
}
});
types.Mistral.Task = Barricade.create({
'@type': Object,
'version': {
'@type': Number,
'@ref': {
to: types.Mistral.Version,
needs: function () {
return types.Mistral.Workbook;
},
resolver: function(json, parentObj) {
return parentObj.get('version')
}
}
},
'name': {'@type': String},
'parameters': {
'@type': Object,
'@required': false,
'?': {'@type': String}
},
'publish': {
'@type': String,
'@required': false
},
'policies': {
'@class': types.Mistral.Policy,
'@required': false
},
'requires': { // array of Task-s existing in the same workflow
'@type': Array,
'@required': false,
'*': {'@type': String}
},
'on-complete': {
'@type': String,
'@required': false
},
'on-success': {
'@type': String,
'@required': false
},
'on-error': {
'@type': String,
'@required': false
}
});
types.Mistral.Tasks = Barricade.MutableObject.extend({
create: function(json, parameters) {
var self = Barricade.MutableObject.create.call(this);
types.base.AcceptsMixin.call(self, [
{
label: 'Action-based',
value: types.Mistral.ActionTask
}, {
label: 'Workflow-based',
value: types.Mistral.WorkflowTask
}
]);
return self;
}
}, {
'@type': Object,
'?': {'@class': types.Mistral.Task}
});
types.Mistral.WorkflowTask = types.Mistral.Task.extend({},
{
'workflow': {
'@type': String,
'@required': false
}
});
types.Mistral.ActionTask = types.Mistral.Task.extend({},
{
'action': {
'@type': String,
'@required': false
}
});
types.Mistral.Workflow = Barricade.create({
'@type': Object,
'version': {
'@type': Number,
'@ref': {
to: types.Mistral.Version,
needs: function () {
return types.Mistral.Workbook;
},
resolver: function(json, parentObj) {
return parentObj.get('version')
}
}
},
'name': {'@type': String},
'type': {
'@type': String,
'@enum': ['reverse', 'direct'],
'@default': 'direct'
},
'parameters': {
'@type': Object,
'@required': false,
'?': {'@type': String}
},
'output': {
'@type': String,
'@required': false
},
'tasks': {
'@class': types.Mistral.Tasks
}
});
types.Mistral.Workbook = Barricade.create({
'@type': Object,
'version': {
'@class': types.Mistral.Version
},
'description': {
'@type': String,
'@required': false
},
'actions': {
'@type': Array,
'@required': false,
'*': {
'@class': types.Mistral.Action
}
},
'workflows': {
'@type': Array,
'*': {
'@class': types.Mistral.Workflow
}
}
});