Restrict existing Action/Workflow to the existing ones

Pull the list of existing Workflows or Actions using the @ref facility
of Barricade.

Closes-bug: #1446228
Change-Id: I0d2dbbe2735104e86cc22507c5f66793294c5b0b
This commit is contained in:
Timur Sufiev 2015-06-16 19:01:07 +03:00
parent 854304924f
commit bcc69f218d
3 changed files with 235 additions and 106 deletions

View File

@ -148,7 +148,7 @@
'@meta': { '@meta': {
'index': 1, 'index': 1,
'row': 0, 'row': 0,
autocompletionUrl: '/project/mistral/actions/types' 'autocompletionUrl': '/project/mistral/actions/types'
} }
}) })
}, },
@ -380,20 +380,94 @@
models.ActionTaskMixin = Barricade.Blueprint.create(function() { models.ActionTaskMixin = Barricade.Blueprint.create(function() {
return this.extend({}, { return this.extend({}, {
'action': { 'action': {
'@class': fields.string.extend({}, { '@class': fields.string.extend({
create: function(json, parameters) {
var self = fields.string.create.call(this, json, parameters),
actionsCls = Barricade.create({
'@type': String,
'@ref': {
to: function() {
return models.Actions;
},
needs: function() {
return models.Workbook;
},
getter: function(data) {
return data.needed.get('actions');
}
}
});
self._actions = actionsCls.create().on(
'replace', function(newValue) {
self._actions = newValue;
self._actions.on('change', function() {
self._choices = self._actions.getIDs();
self.resetValues();
});
self._actions.emit('change');
});
return self;
},
_choices: null
}, {
'@enum': function() {
if (!this._choices) {
this._choices = [];
this.emit('_resolveUp', this._actions);
}
return this._choices;
},
'@meta': { '@meta': {
'row': 0, 'row': 0,
'index': 1 'index': 1
} }
}) })
} }
}); })
}); });
models.WorkflowTaskMixin = Barricade.Blueprint.create(function() { models.WorkflowTaskMixin = Barricade.Blueprint.create(function() {
return this.extend({}, { return this.extend({}, {
'workflow': { 'workflow': {
'@class': fields.string.extend({}, { '@class': fields.string.extend({
create: function(json, parameters) {
var self = fields.string.create.call(this, json, parameters),
workflowsCls = Barricade.create({
'@type': String,
'@ref': {
to: function() {
return models.Workflows;
},
needs: function() {
return models.Workbook;
},
getter: function(data) {
return data.needed.get('workflows');
}
}
});
self._workflows = workflowsCls.create().on(
'replace', function(newValue) {
self._workflows = newValue;
self._workflows.on('change', function() {
self._choices = self._workflows.getIDs();
self.resetValues();
});
self._workflows.emit('change');
});
return self;
},
_choices: null
}, {
'@enum': function() {
if ( !this._choices ) {
this._choices = [];
this.emit('_resolveUp', this._workflows);
}
return this._choices;
},
'@meta': { '@meta': {
'row': 0, 'row': 0,
'index': 1 'index': 1
@ -542,6 +616,44 @@
return workflowTypes[type].create(json, parameters); return workflowTypes[type].create(json, parameters);
} }
models.Actions = fields.dictionary.extend({}, {
'@required': false,
'@meta': {
'index': 3,
'panelIndex': 1
},
'?': {
'@class': models.Action
}
});
models.Workflows = fields.dictionary.extend({
create: function(json, parameters) {
var self = fields.dictionary.create.call(this, json, parameters);
self.on('childChange', function(child, op) {
if ( op === 'workflowType' ) {
var workflowId = child.getID(),
workflowPos = self.getPosByID(workflowId),
params = child._parameters,
workflowData = child.toJSON();
params.wfType = child.type;
params.id = workflowId;
self.set(workflowPos, workflowFactory(workflowData, params));
}
});
return self;
}
}, {
'@meta': {
'index': 4,
'panelIndex': 2
},
'?': {
'@class': models.Workflow,
'@factory': workflowFactory
}
});
models.Workbook = fields.frozendict.extend({ models.Workbook = fields.frozendict.extend({
toYAML: function() { toYAML: function() {
return jsyaml.dump(this.toJSON({pretty: true})); return jsyaml.dump(this.toJSON({pretty: true}));
@ -583,44 +695,10 @@
}) })
}, },
'actions': { 'actions': {
'@class': fields.dictionary.extend({}, { '@class': models.Actions
'@required': false,
'@meta': {
'index': 3,
'panelIndex': 1
},
'?': {
'@class': models.Action
}
})
}, },
'workflows': { 'workflows': {
'@class': fields.dictionary.extend({ '@class': models.Workflows
create: function(json, parameters) {
var self = fields.dictionary.create.call(this, json, parameters);
self.on('childChange', function(child, op) {
if ( op === 'workflowType' ) {
var workflowId = child.getID(),
workflowPos = self.getPosByID(workflowId),
params = child._parameters,
workflowData = child.toJSON();
params.wfType = child.type;
params.id = workflowId;
self.set(workflowPos, workflowFactory(workflowData, params));
}
});
return self;
}
}, {
'@meta': {
'index': 4,
'panelIndex': 2
},
'?': {
'@class': models.Workflow,
'@factory': workflowFactory
}
})
} }
}); });

View File

@ -261,43 +261,78 @@ var Barricade = (function () {
* @mixin * @mixin
* @memberof Barricade * @memberof Barricade
*/ */
Deferrable = Blueprint.create(function (schema) { Deferrable = Blueprint.create(function () {
var self = this, var existingCreate = this.create;
deferred;
function resolver(neededValue) { this.create = function() {
var ref = schema['@ref'].resolver(self, neededValue); var self = existingCreate.apply(this, arguments),
if (ref === undefined) { schema = self._schema,
logError('Could not resolve ', JSON.stringify(self.toJSON())); needed,
} deferred = schema.hasOwnProperty('@ref')
return ref; ? Deferred.create(schema['@ref'].needs, getter, resolver)
} : null;
if (schema.hasOwnProperty('@ref')) { if (schema.hasOwnProperty('@ref') && !schema['@ref'].processor) {
deferred = Deferred.create(schema['@ref'].needs, resolver); schema['@ref'].processor = function (o) { return o.val; };
}
this.resolveWith = function (obj) {
var allResolved = true;
if (deferred && !deferred.isResolved()) {
if (deferred.needs(obj)) {
this.emit('replace', deferred.resolve(obj));
} else {
allResolved = false;
}
} }
if (this.instanceof(Container)) { function getter(neededVal) {
this.each(function (index, value) { return schema['@ref'].getter(
if (!value.resolveWith(obj)) { {standIn: self, needed: neededVal});
}
function resolver(retrievedValue) {
self.emit('replace', schema['@ref'].processor({
val: retrievedValue,
standIn: self,
needed: needed
}));
}
self.resolveWith = function (obj) {
var allResolved = true;
if (deferred && !deferred.isResolved()) {
if (deferred.needs(obj)) {
needed = obj;
deferred.resolve(obj);
} else {
allResolved = false; allResolved = false;
} }
}); }
}
return allResolved; if (this.instanceof(Container)) {
this.each(function (index, value) {
if (!value.resolveWith(obj)) {
allResolved = false;
}
});
}
return allResolved;
};
self.isPlaceholder = function () {
return !!deferred;
};
return self;
}; };
this.isValidRef = function(instance) {
var clsRef = this._schema['@ref'];
if (!clsRef) {
return false;
}
if (typeof clsRef.to === 'function') {
return this._safeInstanceof(instance, clsRef.to());
} else if (typeof clsRef.to === 'object') {
return this._safeInstanceof(instance, clsRef.to);
}
throw new Error('Ref.to was ' + clsRef.to);
};
return this;
}); });
/** /**
@ -407,7 +442,7 @@ var Barricade = (function () {
this.emit = function (eventName) { this.emit = function (eventName) {
var args = arguments; // Must come from correct scope var args = arguments; // Must come from correct scope
if (events.hasOwnProperty(eventName)) { if (events.hasOwnProperty(eventName)) {
events[eventName].forEach(function (callback) { events[eventName].slice().forEach(function (callback) {
// Call with emitter as context and pass all but eventName // Call with emitter as context and pass all but eventName
callback.apply(this, Array.prototype.slice.call(args, 1)); callback.apply(this, Array.prototype.slice.call(args, 1));
}, this); }, this);
@ -469,10 +504,11 @@ var Barricade = (function () {
Callback to execute when resolve happens. Callback to execute when resolve happens.
* @returns {Barricade.Deferred} * @returns {Barricade.Deferred}
*/ */
create: function (classGetter, onResolve) { create: function (classGetter, getter, onResolve) {
var self = Object.create(this); var self = Object.create(this);
self._isResolved = false; self._isResolved = false;
self._classGetter = classGetter; self._classGetter = classGetter;
self._getter = getter;
self._onResolve = onResolve; self._onResolve = onResolve;
return self; return self;
}, },
@ -502,17 +538,25 @@ var Barricade = (function () {
* @param obj * @param obj
*/ */
resolve: function (obj) { resolve: function (obj) {
var ref; var self = this,
neededValue;
function doResolve(realNeededValue) {
neededValue.off('replace', doResolve);
self._onResolve(realNeededValue);
self._isResolved = true;
}
if (this._isResolved) { if (this._isResolved) {
throw new Error('Deferred already resolved'); throw new Error('Deferred already resolved');
} }
ref = this._onResolve(obj); neededValue = this._getter(obj);
if (ref !== undefined) { if (neededValue.isPlaceholder()) {
this._isResolved = true; neededValue.on('replace', doResolve);
return ref; } else {
doResolve(neededValue);
} }
} }
}; };
@ -537,7 +581,7 @@ var Barricade = (function () {
* @mixes Barricade.Identifiable * @mixes Barricade.Identifiable
* @extends Barricade.Identifiable * @extends Barricade.Identifiable
*/ */
Base = Extendable.call(InstanceofMixin.call({ Base = Deferrable.call(Extendable.call(InstanceofMixin.call({
/** /**
* Creates a `Base` instance * Creates a `Base` instance
* @memberof Barricade.Base * @memberof Barricade.Base
@ -564,7 +608,6 @@ var Barricade = (function () {
Observable.call(self); Observable.call(self);
Omittable.call(self, isUsed); Omittable.call(self, isUsed);
Deferrable.call(self, schema);
Validatable.call(self, schema); Validatable.call(self, schema);
if (schema.hasOwnProperty('@enum')) { if (schema.hasOwnProperty('@enum')) {
@ -625,7 +668,7 @@ var Barricade = (function () {
* @private * @private
*/ */
_safeInstanceof: function (instance, class_) { _safeInstanceof: function (instance, class_) {
return typeof instance === 'object' && return getType(instance) === Object &&
('instanceof' in instance) && ('instanceof' in instance) &&
instance.instanceof(class_); instance.instanceof(class_);
}, },
@ -695,7 +738,7 @@ var Barricade = (function () {
? this._getPrettyJSON(options) ? this._getPrettyJSON(options)
: this._getJSON(options); : this._getJSON(options);
} }
})); })));
/** /**
* @class * @class
@ -714,9 +757,15 @@ var Barricade = (function () {
var self = Base.create.call(this, json, parameters); var self = Base.create.call(this, json, parameters);
return self.on('_addedElement', function (key) { return self.on('_addedElement', function (key) {
// every time a new element is added we need to set the
// listeners for it and try to resolve any references,
// passing the resolution upward if needed
self._attachListeners(key); self._attachListeners(key);
self._tryResolveOn(self.get(key)); self._tryResolveOn(self.get(key));
}).each(function (index, value) { }).each(function (index, value) {
// also we bind the handlers and do reference resolution
// for all the children that are already here (no upward
// resolution here)
self._attachListeners(index); self._attachListeners(index);
value.resolveWith(self); value.resolveWith(self);
}); });
@ -776,21 +825,8 @@ var Barricade = (function () {
* @private * @private
*/ */
_isCorrectType: function (instance, class_) { _isCorrectType: function (instance, class_) {
var self = this;
function isRefTo() {
if (typeof class_._schema['@ref'].to === 'function') {
return self._safeInstanceof(instance,
class_._schema['@ref'].to());
} else if (typeof class_._schema['@ref'].to === 'object') {
return self._safeInstanceof(instance,
class_._schema['@ref'].to);
}
throw new Error('Ref.to was ' + class_._schema['@ref'].to);
}
return this._safeInstanceof(instance, class_) || return this._safeInstanceof(instance, class_) ||
(class_._schema.hasOwnProperty('@ref') && isRefTo()); class_.isValidRef(instance);
}, },
/** /**

View File

@ -11,22 +11,37 @@
}); });
var restrictedChoicesMixin = Barricade.Blueprint.create(function() { var restrictedChoicesMixin = Barricade.Blueprint.create(function() {
var values = this.getEnumValues(), var self = this,
labels = this.getEnumLabels(), values, labels, items;
function fillItems() {
values = self.getEnumValues();
labels = self.getEnumLabels();
items = {}; items = {};
values.forEach(function(value, index) { values && values.forEach(function (value, index) {
items[value] = labels[index]; items[value] = labels[index];
}); });
}
this.getLabel = function(value) { this.getLabel = function(value) {
if ( values === undefined ) {
fillItems();
}
return items[value]; return items[value];
}; };
this.getValues = function() { this.getValues = function() {
if ( values === undefined ) {
fillItems();
}
return values; return values;
}; };
this.resetValues = function() {
values = undefined;
};
this.setType('choices'); this.setType('choices');
return this; return this;
}); });
@ -100,16 +115,15 @@
}, {'@type': String}); }, {'@type': String});
var autoCompletionMixin = Barricade.Blueprint.create(function(url) { var autoCompletionMixin = Barricade.Blueprint.create(function(url) {
var suggestions = []; var self = this;
this.getSuggestions = function() { return []; };
$http.get(url).success(function(data) { $http.get(url).success(function(data) {
suggestions = data; self.getSuggestions = function() {
return data;
};
}); });
this.getSuggestions = function() {
return suggestions;
};
return this; return this;
}); });
@ -251,8 +265,9 @@
dictionary: dictionaryModel, dictionary: dictionaryModel,
frozendict: frozendictModel, frozendict: frozendictModel,
directeddictionary: directedDictionaryModel, directeddictionary: directedDictionaryModel,
autocompletionmixin: autoCompletionMixin,
wildcard: wildcardMixin // use for most general type-checks wildcard: wildcardMixin // use for most general type-checks
}; };
}]) }])
})(); })();