Repository: ambari Updated Branches: refs/heads/trunk 9abe8da6e -> 3e5185aca
AMBARI-19622. Need abilities to add a custom action node and import a workflow xml with custom action (Padma Priya via pallavkul) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/3e5185ac Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/3e5185ac Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/3e5185ac Branch: refs/heads/trunk Commit: 3e5185acaea24bfeb28f4b10da6d3a94c90dcacd Parents: 9abe8da Author: pallavkul <pallav....@gmail.com> Authored: Mon Jan 23 15:00:03 2017 +0530 Committer: pallavkul <pallav....@gmail.com> Committed: Mon Jan 23 15:00:03 2017 +0530 ---------------------------------------------------------------------- .../ui/app/components/flow-designer.js | 11 +++ .../ui/app/components/workflow-action-editor.js | 77 ++++++++++++++++---- .../ui/app/components/workflow-actions.js | 5 ++ .../ui/app/domain/action-type-resolver.js | 10 ++- .../ui/app/domain/actionjob_hanlder.js | 22 +++++- .../resources/ui/app/domain/node-handler.js | 10 +-- .../src/main/resources/ui/app/styles/app.less | 4 + .../app/templates/components/flow-designer.hbs | 25 +++++++ .../components/workflow-action-editor.hbs | 16 ++++ .../templates/components/workflow-actions.hbs | 3 + .../main/resources/ui/app/utils/common-utils.js | 4 + .../main/resources/ui/app/utils/constants.js | 3 +- 12 files changed, 169 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js index 8bbe831..1822a20 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js @@ -775,6 +775,17 @@ export default Ember.Component.extend(FindNodeMixin, Validations, { this.set('showCreateKillNode', false); }, addNode(type){ + if(type === 'custom'){ + this.$('#customTypeModal').modal('show'); + }else{ + this.send('addAction', type); + } + }, + createCustomAction(type){ + this.send('addAction', type); + this.set('customActionType', ''); + }, + addAction(type){ this.createSnapshot(); var currentTransition=this.get("currentTransition"); this.get("workflow").addNode(this.findTransition(this.get("workflow").startNode, currentTransition.sourceNodeId, currentTransition.targetNode.id),type); http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js index 8a3c7cf..f2d3ba8 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js @@ -17,6 +17,7 @@ import Ember from 'ember'; import Constants from '../utils/constants'; +import CommonUtils from '../utils/common-utils'; import {SlaInfo} from '../domain/sla-info'; export default Ember.Component.extend( Ember.Evented,{ @@ -40,6 +41,7 @@ export default Ember.Component.extend( Ember.Evented,{ clonedActionModel : {}, showingFileBrowser : false, childComponents : new Map(), + errors : Ember.A([]), isActionNode : Ember.computed('nodeType',function(){ if(this.get('nodeType') === 'action'){ return true; @@ -58,13 +60,43 @@ export default Ember.Component.extend( Ember.Evented,{ return this.get('actionIcons')[this.get('actionType')]; }), saveClicked : false, - containsUnsupportedProperties : Ember.computed('actionModel.unsupportedProperties', function(){ - return this.get('actionModel.unsupportedProperties') ? !Ember.isEmpty(Object.keys(this.get('actionModel.unsupportedProperties'))) : false; - }), - unsupportedPropertiesXml : Ember.computed('actionModel.unsupportedProperties', function(){ - if(this.get('containsUnsupportedProperties')){ - var x2js = new X2JS(); + unsupportedPropertiesXml : Ember.computed('actionModel.unsupportedProperties', { + get(key){ + let x2js = new X2JS(); return vkbeautify.xml(x2js.json2xml_str(this.get('actionModel.unsupportedProperties'))); + }, + set(key, value) { + let x2js = new X2JS(); + var temp = x2js.xml_str2json(vkbeautify.xmlmin(`<unsupportedProperties>${value}</unsupportedProperties>`)); + this.set('actionModel.unsupportedProperties', temp.unsupportedProperties); + Object.keys(this.get('actionModel.unsupportedProperties')).forEach(key =>{ + this.set(`actionModel.${key}`, this.get(`actionModel.unsupportedProperties.${key}`)); + }); + return value; + } + }), + actionXml : Ember.computed('actionModel', { + get(key) { + let x2js = new X2JS(); + var startTag = `<${this.get('actionType')}`; + Object.keys(this.get('actionModel')).forEach(key => { + if(key.startsWith('_')){ + startTag = `${startTag} ${key.substr(1)}="${this.get('actionModel')[key]}"`; + } + }); + startTag = `${startTag}>`; + return vkbeautify.xml(`${startTag}${x2js.json2xml_str(this.get('actionModel'))}</${this.get('actionType')}>`); + }, + set(key, value) { + let x2js = new X2JS(); + this.set('errors', Ember.A([])); + let temp = x2js.xml_str2json(vkbeautify.xmlmin(value)); + if(temp){ + this.set('actionModel', temp[this.get('actionType')]); + }else{ + this.get('errors').pushObject({message:'Action Xml is syntatically incorrect'}); + } + return value; } }), fileBrowser : Ember.inject.service('file-browser'), @@ -87,15 +119,30 @@ export default Ember.Component.extend( Ember.Evented,{ errorNode : errorNode }); this.set('transition',transition); - if (Ember.isBlank(this.get("actionModel.jobTracker"))){ - this.set('actionModel.jobTracker',Constants.rmDefaultValue); - } - if (Ember.isBlank(this.get("actionModel.nameNode"))){ - this.set('actionModel.nameNode','${nameNode}'); + if(CommonUtils.isSupportedAction(this.get('actionType'))){ + if (Ember.isBlank(this.get("actionModel.jobTracker"))){ + this.set('actionModel.jobTracker',Constants.rmDefaultValue); + } + if (Ember.isBlank(this.get("actionModel.nameNode"))){ + this.set('actionModel.nameNode','${nameNode}'); + } } - if(this.get('nodeType') === 'action' && this.get('actionModel.slaInfo') === undefined){ + if(this.get('nodeType') === 'action' && CommonUtils.isSupportedAction(this.get('actionType')) && this.get('actionModel.slaInfo') === undefined){ this.set('actionModel.slaInfo', SlaInfo.create({})); } + if(!CommonUtils.isSupportedAction(this.get('actionType')) && !this.get('actionModel.slaInfo')){ + this.set('customSlaInfo', SlaInfo.create({})); + }else{ + this.set('customSlaInfo', this.get('actionModel.slaInfo')); + this.set('customSlaEnabled', this.get('actionModel.slaEnabled')); + delete this.get('actionModel').slaInfo; + delete this.get('actionModel').slaEnabled; + } + if(this.get('actionModel.unsupportedProperties') && !Ember.isEmpty(Object.keys(this.get('actionModel.unsupportedProperties')))){ + this.set('containsUnsupportedProperties', true); + }else{ + this.set('containsUnsupportedProperties', false); + } }.on('init'), initialize : function(){ this.$('#action_properties_dialog').modal({ @@ -150,10 +197,14 @@ export default Ember.Component.extend( Ember.Evented,{ }, save () { var isChildComponentsValid = this.validateChildrenComponents(); - if(this.get('validations.isInvalid') || !isChildComponentsValid) { + if(this.get('validations.isInvalid') || !isChildComponentsValid || this.get('errors').length > 0) { this.set('showErrorMessage', true); return; } + if(!CommonUtils.isSupportedAction(this.get('actionType'))){ + this.set('actionModel.slaInfo', this.get('customSlaInfo')); + this.set('actionModel.slaEnabled', this.get('customSlaEnabled')); + } this.processMultivaluedComponents(); this.processStaticProps(); this.$('#action_properties_dialog').modal('hide'); http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js index 7c78eea..2f8cdaa 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js @@ -16,10 +16,15 @@ */ import Ember from 'ember'; +import Constants from '../utils/constants'; + export default Ember.Component.extend({ clipboardHasContents : Ember.computed.oneWay('clipboard', function(){ return !Ember.isEmpty(this.get('clipboard')); }), + initialize : function(){ + this.set('customActionEnabled', Constants.customActionEnabled); + }.on('init'), actions : { addAction : function(type){ this.$(".dr_action").css("background-color", "#fff"); http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/domain/action-type-resolver.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/action-type-resolver.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/action-type-resolver.js index c25b953..8cbcfaf 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/action-type-resolver.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/action-type-resolver.js @@ -55,7 +55,15 @@ var ActionTypeResolver=Ember.Object.extend({ return resolvedType; }, getActionJobHandler(jobType){ - return this.actionJobHandlerMap.get(jobType); + if(this.actionJobHandlerMap.has(jobType)) { + return this.actionJobHandlerMap.get(jobType); + }else{ + var customActionJobHandler = actionJobHandler.CustomActionJobHandler.create({ + actionType : jobType + }); + this.actionJobHandlerMap.set(jobType,customActionJobHandler); + return customActionJobHandler; + } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js index 4cc89ef..34a9a4a 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js @@ -337,6 +337,26 @@ var MapRedActionJobHandler=ActionJobHandler.extend({ } }); +var CustomActionJobHandler=ActionJobHandler.extend({ + actionType:'', + mapping:null, + init(){ + this.mapping=[]; + }, + handleImport(actionNode,json){ + actionNode.set('domain', json); + }, + handle(nodeDomain,nodeObj,nodeName){ + var customDomain = {}; + Object.keys(nodeDomain).forEach(key =>{ + if(key !== 'slaInfo' && key !== 'slaEnabled' && key!=='credentials'){ + customDomain[key] = nodeDomain[key]; + } + }); + nodeObj[this.get("actionType")] = customDomain; + } +}); + var FSActionJobHandler=ActionJobHandler.extend({ actionType:"fs", mapping:null, @@ -544,4 +564,4 @@ var FSActionJobHandler=ActionJobHandler.extend({ }); } }); -export{ActionJobHandler,JavaActionJobHandler,PigActionJobHandler,HiveActionJobHandler,SqoopActionJobHandler,ShellActionJobHandler, EmailActionJobHandler,SparkActionJobHandler,MapRedActionJobHandler, Hive2ActionJobHandler, SubWFActionJobHandler, DistCpJobHandler, SshActionJobHandler, FSActionJobHandler}; +export{ActionJobHandler,JavaActionJobHandler,PigActionJobHandler,HiveActionJobHandler,SqoopActionJobHandler,ShellActionJobHandler, EmailActionJobHandler,SparkActionJobHandler,MapRedActionJobHandler, Hive2ActionJobHandler, SubWFActionJobHandler, DistCpJobHandler, SshActionJobHandler, FSActionJobHandler, CustomActionJobHandler}; http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js index 6bc305a..28ea527 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js @@ -125,16 +125,16 @@ var ActionNodeHandler= NodeHandler.extend({ return actionNode; } var actionJobHandler=this.get("actionTypeResolver").getActionJobHandler(actionType); - if (!actionJobHandler){ - console.error("cannot handle unsupported action type:"+actionType+" for "+nodeJson._name);//TODO error handling... - return actionNode; + if(actionJobHandler){ + actionJobHandler.handleImport(actionNode,nodeJson[actionType]); } - actionJobHandler.handleImport(actionNode,nodeJson[actionType]); if (nodeJson.info && nodeJson.info.__prefix==="sla") { actionNode.domain.slaEnabled=true; this.slaMapper.handleImport(actionNode.domain,nodeJson.info,"slaInfo"); } - actionNode.domain.credentials=nodeJson._cred; + if(nodeJson._cred){ + actionNode.domain.credentials=nodeJson._cred; + } return actionNode; }, handleImportTransitions(node,json,nodeMap){ http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less b/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less index 05bdb5a..d91eb3b 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less +++ b/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less @@ -1632,3 +1632,7 @@ input:invalid { padding: 3px; overflow-y: auto; } +.custom-action-xml{ + width: 100%; + min-height: 175px; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs index 95c8c3b..38d8eaf 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs +++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs @@ -377,3 +377,28 @@ {{#if showKillNodeManager}} {{#killnode-manager killNodes=workflow.killNodes killNode=killNode createKillnodeError=createKillnodeError createKillNode="createKillNode" deleteNode="deleteNode" addKillNodeMode=addKillNodeMode editMode=editMode closeKillNodeManager="closeKillNodeManager"}}{{/killnode-manager}} {{/if}} + +<div id="customTypeModal" class="modal fade" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal">×</button> + <h4 class="modal-title">Custom Action</h4> + </div> + <div class="modal-body"> + <form class="form-horizontal"> + <div class="form-group"> + <label for="inputEmail" class="control-label col-xs-2">Type</label> + <div class="col-xs-7"> + {{input type="text" class="form-control" name="job-tracker" value=customActionType placeholder="Custom Action Type"}} + </div> + </div> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-primary" data-dismiss="modal" {{action 'createCustomAction' customActionType}}>OK</button> + </div> + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-action-editor.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-action-editor.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-action-editor.hbs index b36578d..bb089c0 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-action-editor.hbs +++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-action-editor.hbs @@ -64,6 +64,22 @@ {{#fs-action actionModel=actionModel transition=transition killNodes=killNodes openFileBrowser="openFileBrowser" register="registerChild" addKillNode="addKillNode" currentNode=currentNode credentials=credentials}}{{/fs-action}} {{else if (eq actionType 'sub-workflow')}} {{#sub-workflow actionModel=actionModel transition=transition killNodes=killNodes openFileBrowser="openFileBrowser" register="registerChild" addKillNode="addKillNode" currentNode=currentNode credentials=credentials}}{{/sub-workflow}} + {{else}} + <div class="panel panel-default"> + <div class="panel-heading">Action XML</div> + <div class="panel-body handlerPanel"> + {{designer-errors errors=errors}} + {{textarea class="custom-action-xml" value=actionXml}} + </div> + </div> + <div class="panel panel-default"> + <div class="panel-heading">Transition</div> + <div class="panel-body handlerPanel"> + {{#transition-config transition=transition killNodes=killNodes currentNode=currentNode}}{{/transition-config}} + </div> + </div> + {{#action-credential-config credentials=credentials actionCredentials=actionModel.credentials}}{{/action-credential-config}} + {{#sla-info slaInfo=customSlaInfo slaEnabled=customSlaEnabled}}{{/sla-info}} {{/if}} {{#if containsUnsupportedProperties}} <div id="unsupported-props" class=" panel panel-default"> http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-actions.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-actions.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-actions.hbs index 6d672b4..badf320 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-actions.hbs +++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/workflow-actions.hbs @@ -55,6 +55,9 @@ <li class="dr_action disabled hide" data-name="Stream" data-type="stream"> <i class="fa fa-exchange"></i> Stream </li> <li {{action 'addAction' 'email'}} class="dr_action enabled" data-name="Email" data-type="email"> <i class="fa fa-envelope"></i> Email </li> <li {{action 'addAction' 'fs'}} class="dr_action enabled" data-name="fs" data-type="fs"> <i class="fa fa-folder-o"></i> FS </li> + {{#if customActionEnabled}} + <li {{action 'addAction' 'custom'}} class="dr_action enabled" data-name="custom" data-type="custom"> <i class="fa fa-magic" aria-hidden="true"></i> Custom </li> + {{/if}} </ul> </div> <div class="clearfix"></div> http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/utils/common-utils.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/utils/common-utils.js b/contrib/views/wfmanager/src/main/resources/ui/app/utils/common-utils.js index e3be7da..8cc40d6 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/utils/common-utils.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/utils/common-utils.js @@ -16,6 +16,7 @@ */ import Ember from 'ember'; +import Constants from '../utils/constants'; export default Ember.Object.create({ extractSchemaVersion(xmlns){ return xmlns.substring(xmlns.lastIndexOf(":")+1); @@ -25,5 +26,8 @@ export default Ember.Object.create({ }, setTestContext(context){ window.flowDesignerTestContext=context; + }, + isSupportedAction(actionType){ + return Object.values(Constants.actions).findBy('name', actionType)? true : false; } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/3e5185ac/contrib/views/wfmanager/src/main/resources/ui/app/utils/constants.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/utils/constants.js b/contrib/views/wfmanager/src/main/resources/ui/app/utils/constants.js index fc20359..9126819 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/utils/constants.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/utils/constants.js @@ -88,5 +88,6 @@ export default Ember.Object.create({ persistWorkInProgressInterval : 30000, elConstants : [ '${YEAR}', '${MONTH}', '${DAY}', '${HOUR}', '${MINUTE}' - ] + ], + customActionEnabled : false });