/** * Created by tsufiev on 2/24/15. */ (function() { 'use strict'; angular .module('mistral') .factory('mistral.workbook.models', ModelsService); ModelsService.$inject = ['merlin.field.models', 'merlin.utils']; function ModelsService(fields, utils) { var models = {}; function varlistValueFactory(json, parameters) { var type = Barricade.getType(json); if ( angular.isUndefined(json) || type === String ) { return fields.string.create(json, parameters); } else if ( type === Array ) { return fields.list.extend({}, { '*': {'@class': fields.string} }).create(json, parameters); } else if ( type === Object ) { return fields.dictionary.extend({}, { '?': {'@class': fields.string} }).create(json, parameters); } } models.varlist = fields.list.extend({ create: function(json, parameters) { var self = fields.list.create.call(this, json, parameters); self.setType('varlist'); self.on('childChange', function(child, op) { if ( op == 'empty' ) { self.each(function(index, item) { if ( child === item ) { self.remove(index); } }); } }); return self; } }, { '*': { '@class': fields.frozendict.extend({ create: function(json, parameters) { var self = fields.frozendict.create.call(this, json, parameters); self.on('childChange', function(child) { if ( child.instanceof(Barricade.Enumerated) ) { // type change var value = self.get('value'); switch ( child.get() ) { case 'string': self.set('value', varlistValueFactory('')); break; case 'list': self.set('value', varlistValueFactory([''])); break; case 'dictionary': self.set('value', varlistValueFactory({'key1': ''})); break; } } else if ( child.instanceof(Barricade.Arraylike) && !child.length() ) { self.emit('change', 'empty'); } }); return self; }, _getPrettyJSON: function() { var json = fields.frozendict._getPrettyJSON.apply(this, arguments); return json.value; } }, { 'type': { '@class': fields.string.extend({}, { '@enum': ['string', 'list', 'dictionary'], '@default': 'string' }) }, 'value': { '@class': fields.wildcard, '@factory': varlistValueFactory } }) } }); models.yaqllist = fields.list.extend({ create: function(json, parameters) { var self = fields.list.create.call(this, json, parameters); self.setType('yaqllist'); return self; } }, { '*': { '@class': fields.frozendict.extend({}, { 'yaql': { '@class': fields.string }, 'action': { '@class': fields.string } }) } }); models.Action = fields.frozendict.extend({ create: function(json, parameters) { var self = fields.frozendict.create.call(this, json, parameters); var base = self.get('base'); base.on('change', function(operation) { var argsEntry, pos, entry; if ( operation != 'id' ) { pos = base._collection.getPosByID(base.get()); if ( pos > -1 ) { entry = self.get('base-input'); argsEntry = base._collection.get(pos); entry.resetKeys(argsEntry.toJSON()); } } }); return self; } }, { 'base': { '@class': fields.linkedcollection.extend({ create: function(json, parameters) { parameters = Object.create(parameters); parameters.toCls = models.StandardActions; parameters.neededCls = models.Root; parameters.substitutedEntryID = 'standardActions'; return fields.linkedcollection.create.call(this, json, parameters); } }, { '@meta': { 'index': 1, 'row': 0 } }) }, 'base-input': { '@class': fields.dictionary.extend({ create: function(json, parameters) { var self = fields.dictionary.create.call(this, json, parameters); self.setType('frozendict'); return self; } }, { '@required': false, '?': { '@class': fields.string.extend({}, { '@meta': { 'row': 0 } }) }, '@meta': { 'index': 2, 'title': 'Base Input' } }) }, 'input': { '@class': fields.list.extend({}, { '@meta': { 'index': 3 }, '*': {'@class': fields.string} }) }, 'output': { '@class': models.varlist.extend({}, { '@meta': { 'index': 4 } }) } }); models.Task = fields.frozendict.extend({ create: function(json, parameters) { var self = fields.frozendict.create.call(this, json, parameters); self.on('childChange', function(child, op) { if ( child === self.get('type') && op !== 'id' ) { self.emit('change', 'taskType'); } }); return self; }, remove: function() { this.emit('change', 'taskRemove', this.getID()); }, _getPrettyJSON: function() { var json = fields.frozendict._getPrettyJSON.apply(this, arguments); delete json.type; return json; } }, { '@meta': { 'baseKey': 'task', 'baseName': 'Task ', 'group': true, 'additive': false, 'removable': true }, 'type': { '@class': fields.string.extend({}, { '@enum': [{ value: 'action', label: 'Action-based' }, { value: 'workflow', label: 'Workflow-based' }], '@default': 'action', '@meta': { 'index': 0, 'row': 0 } }) }, 'description': { '@class': fields.text.extend({}, { '@meta': { 'index': 2, 'row': 1 } }) }, 'input': { '@class': fields.dictionary.extend({}, { '@meta': { 'index': 4 }, '?': { '@class': fields.string } }) }, 'publish': { '@class': fields.dictionary.extend({}, { '@meta': { 'index': 5 }, '?': { '@class': fields.string } }) }, 'policies': { '@class': fields.frozendict.extend({ _getPrettyJSON: function() { var json = fields.frozendict._getPrettyJSON.apply(this, arguments); json.retry = { count: utils.pop(json, 'retry-count'), delay: utils.pop(json, 'retry-delay'), 'break-on': utils.pop(json, 'retry-break-on') }; return json; } }, { '@meta': { 'index': 9 }, '@required': false, 'wait-before': { '@class': fields.number.extend({}, { '@required': false, '@meta': { 'index': 0, 'row': 0, 'title': 'Wait before' } }) }, 'wait-after': { '@class': fields.number.extend({}, { '@required': false, '@meta': { 'index': 1, 'row': 0, 'title': 'Wait after' } }) }, 'timeout': { '@class': fields.number.extend({}, { '@required': false, '@meta': { 'index': 2, 'row': 1 } }) }, 'retry-count': { '@class': fields.number.extend({}, { '@required': false, '@meta': { 'index': 3, 'row': 2, 'title': 'Retry count' } }) }, 'retry-delay': { '@class': fields.number.extend({}, { '@required': false, '@meta': { 'index': 4, 'row': 2, 'title': 'Retry delay' } }) }, 'retry-break-on': { '@class': fields.number.extend({}, { '@required': false, '@meta': { 'index': 5, 'row': 3, 'title': 'Retry break on' } }) } }) } }); models.ReverseWFTask = models.Task.extend({}, { 'requires': { '@class': fields.string.extend({}, { '@meta': { 'row': 2, 'index': 3 } }) } }); models.DirectWFTask = models.Task.extend({}, { 'on-error': { '@class': fields.list.extend({}, { '@meta': { 'title': 'On error', 'index': 6 }, '*': { '@class': fields.string } }) }, 'on-success': { '@class': fields.list.extend({}, { '@meta': { 'title': 'On success', 'index': 7 }, '*': { '@class': fields.string } }) }, 'on-complete': { '@class': fields.list.extend({}, { '@meta': { 'title': 'On complete', 'index': 8 }, '*': { '@class': fields.string } }) } }); models.ActionTaskMixin = Barricade.Blueprint.create(function() { return this.extend({}, { 'action': { '@class': fields.linkedcollection.extend({ create: function(json, parameters) { parameters = Object.create(parameters); parameters.toCls = models.Actions; parameters.neededCls = models.Workbook; parameters.substitutedEntryID = 'actions'; return fields.linkedcollection.create.call(this, json, parameters); } }, { '@meta': { 'row': 0, 'index': 1 } }) } }); }); models.WorkflowTaskMixin = Barricade.Blueprint.create(function() { return this.extend({}, { 'workflow': { '@class': fields.linkedcollection.extend({ create: function(json, parameters) { parameters = Object.create(parameters); parameters.toCls = models.Workflows; parameters.neededCls = models.Workbook; parameters.substitutedEntryID = 'workflows'; return fields.linkedcollection.create.call(this, json, parameters); } }, { '@meta': { 'row': 0, 'index': 1 } }) } }); }); var taskTypes = { 'direct': models.DirectWFTask, 'reverse': models.ReverseWFTask, 'action': models.ActionTaskMixin, 'workflow': models.WorkflowTaskMixin }; function TaskFactory(json, parameters) { var type = json.type || 'action'; var baseClass = taskTypes[parameters.wfType]; var mixinClass = taskTypes[type]; var taskClass = mixinClass.call(baseClass); return taskClass.create(json, parameters); } models.Workflow = fields.frozendict.extend({ create: function(json, parameters) { var self = fields.frozendict.create.call(this, json, parameters); self.on('childChange', function(child, op) { if ( child === self.get('type') && op !== 'id' ) { self.emit('change', 'workflowType'); } }); return self; } }, { 'type': { '@class': fields.string.extend({}, { '@enum': ['reverse', 'direct'], '@default': 'direct', '@meta': { 'index': 1, 'row': 0 } }) }, 'input': { '@class': fields.list.extend({}, { '@required': false, '@meta': { 'index': 2 }, '*': { '@class': fields.string } }) }, 'output': { '@class': fields.list.extend({}, { '@required': false, '@meta': { 'index': 3 }, '*': { '@class': fields.string } }) }, 'tasks': { '@class': fields.dictionary.extend({ create: function(json, parameters) { var self = fields.dictionary.create.call(this, json, parameters); self.on('childChange', function(child, op, arg) { if ( op === 'taskType' ) { var taskId = child.getID(); var params = child._parameters; var taskPos = self.getPosByID(taskId); var taskData = child.toJSON(); params.id = taskId; self.set(taskPos, TaskFactory(taskData, params)); } else if ( op === 'taskRemove' ) { self.removeItem(arg); } }); return self; } }, { '@meta': { 'index': 5, 'group': true }, '?': { '@class': models.Task, '@factory': TaskFactory } }) } }); models.ReverseWorkflow = models.Workflow.extend({}); models.DirectWorkflow = models.Workflow.extend({}, { 'task-defaults': { '@class': fields.frozendict.extend({}, { '@required': false, '@meta': { 'index': 4, 'group': true, 'additive': false }, 'on-error': { '@class': models.yaqllist.extend({}, { '@meta': { 'title': 'On error', 'index': 0 } }) }, 'on-success': { '@class': models.yaqllist.extend({}, { '@meta': { 'title': 'On success', 'index': 1 } }) }, 'on-complete': { '@class': models.yaqllist.extend({}, { '@meta': { 'title': 'On complete', 'index': 2 } }) } }) } }); var workflowTypes = { 'direct': models.DirectWorkflow, 'reverse': models.ReverseWorkflow }; function workflowFactory(json, parameters) { var type = json.type || 'direct'; parameters.wfType = type; 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(); var workflowPos = self.getPosByID(workflowId); var params = child._parameters; var 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({ toYAML: function() { return jsyaml.dump(this.toJSON({pretty: true})); } }, { 'version': { '@class': fields.string.extend({}, { '@enum': ['2.0'], '@meta': { 'index': 2, 'panelIndex': 0, 'row': 1 }, '@default': '2.0' }) }, 'name': { '@class': fields.string.extend({}, { '@meta': { 'index': 0, 'panelIndex': 0, 'row': 0 }, '@constraints': [ function(value) { return value !== 'workbook1' ? true : 'The sample validation failure.'; } ] }) }, 'description': { '@class': fields.text.extend({}, { '@meta': { 'index': 1, 'panelIndex': 0, 'row': 0 }, '@required': false }) }, 'actions': { '@class': models.Actions }, 'workflows': { '@class': models.Workflows } }); models.StandardActions = Barricade.create({ '@type': Object, '?': { '@type': Array, '*': { '@type': String } } }); models.Root = Barricade.ImmutableObject.extend({}, { '@type': Object, 'standardActions': { '@class': models.StandardActions }, 'workbook': { '@class': models.Workbook } }); return models; } })();