You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2016/10/28 17:45:50 UTC
[11/38] ambari git commit: AMBARI-18691. Improve and Update Workflow
designer to support coordinators and bundles. (Belliraj HB via dipayanb)
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/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 bd2944b..ed743f4 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
@@ -20,17 +20,45 @@ import {Workflow} from '../domain/workflow';
import Constants from '../utils/constants';
import {WorkflowGenerator} from '../domain/workflow-xml-generator';
import {WorkflowImporter} from '../domain/workflow-importer';
+import {WorkflowJsonImporter} from '../domain/workflow-json-importer';
import {WorkflowContext} from '../domain/workflow-context';
-import {DefaultLayoutManager as LayoutManager} from '../domain/default-layout-manager';
-import EmberValidations,{ validator } from 'ember-validations';
+import {JSPlumbRenderer} from '../domain/jsplumb-flow-renderer';
+import {CytoscapeRenderer} from '../domain/cytoscape-flow-renderer';
+import {FindNodeMixin} from '../domain/findnode-mixin';
+import { validator, buildValidations } from 'ember-cp-validations';
+import WorkflowPathUtil from '../domain/workflow-path-util';
+const Validations = buildValidations({
+ 'dataNodes': { /* For Cytoscape */
+ validators: [
+ validator('duplicate-data-node-name', {
+ dependentKeys: ['dataNodes.@each.dataNodeName']
+ })
+ ]
+ },
+ 'workflow.killNodes': {
+ validators: [
+ validator('duplicate-kill-node-name', {
+ dependentKeys: ['workflow.killNodes.@each.name']
+ })
+ ]
+ },
+ 'flattenedNodes': {
+ validators: [
+ validator('duplicate-flattened-node-name', {
+ dependentKeys: ['flattenedNodes.@each.name']
+ })
+ ]
+ }
+});
-export default Ember.Component.extend(EmberValidations,{
+export default Ember.Component.extend(FindNodeMixin, Validations, {
workflowContext : WorkflowContext.create({}),
workflowTitle:"",
previewXml:"",
supportedActionTypes:["java", "hive", "pig", "sqoop", "shell", "spark", "map-reduce", "hive2", "sub-workflow", "distcp", "ssh", "FS"],
workflow:null,
+ hoveredWidget:null,/**/
showingConfirmationNewWorkflow:false,
showingWorkflowConfigProps:false,
workflowSubmitConfigs:{},
@@ -40,106 +68,118 @@ export default Ember.Component.extend(EmberValidations,{
domain:{},
showActionEditor : false,
flattenedNodes: [],
-
+ dataNodes: [], /* For cytoscape */
+ hoveredAction: null,
workflowImporter:WorkflowImporter.create({}),
- designerPlumb:null,
propertyExtractor : Ember.inject.service('property-extractor'),
+ clipboardService : Ember.inject.service('workflow-clipboard'),
+ workspaceManager : Ember.inject.service('workspace-manager'),
showGlobalConfig : false,
showParameterSettings : false,
+ showNotificationPanel : false,
globalConfig : {},
parameters : {},
clonedDomain : {},
clonedErrorNode : {},
validationErrors : [],
- layoutManager:null,
showingFileBrowser : false,
killNode : {},
isWorkflowImporting: false,
isImportingSuccess: true,
- initialize :function(){
- this.designerPlumb=jsPlumb.getInstance({});
- this.layoutManager=LayoutManager.create({});
+ shouldPersist : false,
+ useCytoscape: Constants.useCytoscape,
+ cyOverflow: {},
+ clipboard : Ember.computed.alias('clipboardService.clipboard'),
+ isStackTraceVisible: false,
+ isStackTraceAvailable: false,
+ stackTrace:"",
+ initialize : function(){
+ var id = 'cy-' + Math.ceil(Math.random() * 1000);
+ this.set('cyId', id);
+ this.sendAction('register', this.get('tabInfo'), this);
+ }.on('init'),
+ elementsInserted :function(){
+ if (this.useCytoscape){
+ this.flowRenderer=CytoscapeRenderer.create({id : this.get('cyId')});
+ }else{
+ this.flowRenderer=JSPlumbRenderer.create({});
+ }
+
this.setConentWidth();
this.set('workflow',Workflow.create({}));
if(this.get("xmlAppPath")){
- var workflowXmlPath = this.get("xmlAppPath"), relXmlPath = "", tempArr;
- if(workflowXmlPath.indexOf("://") === -1 && workflowXmlPath.indexOf(":") === -1){
- relXmlPath = workflowXmlPath;
- } else{
- tempArr = workflowXmlPath.split("//")[1].split("/");
- tempArr.splice(0, 1);
- relXmlPath = "/" + tempArr.join("/");
- if(!(relXmlPath.indexOf(".xml") === relXmlPath.length-4)) {
- if(relXmlPath.charAt(relXmlPath.length-1) !== "/"){
- relXmlPath = relXmlPath+ "/" +"workflow.xml";
- } else{
- relXmlPath = relXmlPath+"workflow.xml";
- }
- }
- }
- this.importWorkflow(relXmlPath);
+ this.showExistingWorkflow();
return;
- }else{
+ } else {
this.workflow.initialize();
this.initAndRenderWorkflow();
this.$('#wf_title').focus();
- this.restoreWorkinProgress();
+ if (Constants.autoRestoreWorkflowEnabled){
+ this.restoreWorkflow();
+ }
+ }
+ if(Ember.isBlank(this.get('workflow.name'))){
+ this.set('workflow.name', Ember.copy(this.get('tabInfo.name')));
}
- }.on('didInsertElement'),
- validations: {
- 'flattenedNodes': {
- inline : validator(function() {
- var nodeNames = new Map();
- this.get("validationErrors").clear();
- this.get('flattenedNodes').forEach((item)=>{
- Ember.set(item, "errors", false);
- if(nodeNames.get(item.name)){
- Ember.set(item, "errors", true);
- this.get("validationErrors").pushObject({node:item,message:"Node name should be unique"});
- }else{
- nodeNames.set(item.name, item);
- Ember.set(item, "errors", false);
- }
- if(this.get("supportedActionTypes").indexOf(item.actionType) === -1 && item.type === "action"){
- this.get('validationErrors').pushObject({node : item ,message : item.actionType+" is unsupported"});
- }
- var nodeErrors=item.validateCustom();
- if (nodeErrors.length>0){
- Ember.set(item, "errors", true);
- nodeErrors.forEach(function(errMsg){
- this.get("errors").pushObject({node:item,message:errMsg });
- }.bind(this));
- }
- }.bind(this));
- if(this.get('flattenedNodes').length !== nodeNames.size || this.get("errors").length>0){
- return true;
- }
- })
- },
- "workflow.killnodes": {
- inline : validator(function() {
- let killNodes = [], flag;
- if(this.get("workflow") && this.get("workflow").killNodes){
- killNodes = this.get("workflow").killNodes;
- for(let i=0; i<killNodes.length; i++){
- for(let j=0; j<killNodes.length; j++){
- if(killNodes[i].name === killNodes[j].name && i !== j){
- this.get('validationErrors').pushObject({node : killNodes[j] ,message : "Duplicate killnode"});
- flag = true;
- break;
- }
- }
- if(flag){
- break;
- }
- }
- }
- if (flag){
- return true;
+ }.on('didInsertElement'),
+ restoreWorkflow(){
+ if (!this.get("isNew")){
+ var draftWorkflow=this.getDraftWorkflow();
+ if (draftWorkflow){
+ this.resetDesigner();
+ this.set("workflow",draftWorkflow);
+ this.rerender();
+ this.doValidation();
+ }
+ }
+ },
+ observeXmlAppPath : Ember.observer('xmlAppPath', function(){
+ if(!this.get('xmlAppPath') || null === this.get('xmlAppPath')){
+ return;
+ }else{
+ this.showExistingWorkflow();
+ }
+ }),
+ observeFilePath : Ember.observer('workflowFilePath', function(){
+ if(!this.get('workflowFilePath') || null === this.get('workflowFilePath')){
+ return;
+ }else{
+ this.sendAction('changeFilePath', this.get('tabInfo'), this.get('workflowFilePath'));
+ }
+ }),
+ nameObserver : Ember.observer('workflow.name', function(){
+ if(!this.get('workflow')){
+ return;
+ }else if(this.get('workflow') && Ember.isBlank(this.get('workflow.name'))){
+ if(!this.get('clonedTabInfo')){
+ this.set('clonedTabInfo', Ember.copy(this.get('tabInfo')));
+ }
+ this.sendAction('changeTabName', this.get('tabInfo'), this.get('clonedTabInfo.name'));
+ }else{
+ this.sendAction('changeTabName', this.get('tabInfo'), this.get('workflow.name'));
+ }
+ }),
+ showParentWorkflow(type, path){
+ this.sendAction('openTab', type, path);
+ },
+ showExistingWorkflow(){
+ var workflowXmlPath = this.get("xmlAppPath"), relXmlPath = "", tempArr;
+ if(workflowXmlPath.indexOf("://") === -1 && workflowXmlPath.indexOf(":") === -1){
+ relXmlPath = workflowXmlPath;
+ } else{
+ tempArr = workflowXmlPath.split("//")[1].split("/");
+ tempArr.splice(0, 1);
+ relXmlPath = "/" + tempArr.join("/");
+ if(relXmlPath.indexOf(".xml") !== relXmlPath.length-4) {
+ if(relXmlPath.charAt(relXmlPath.length-1) !== "/"){
+ relXmlPath = relXmlPath+ "/" +"workflow.xml";
+ } else{
+ relXmlPath = relXmlPath+"workflow.xml";
}
- })
+ }
}
+ this.importWorkflow(relXmlPath);
},
setConentWidth(){
var offset = 120;
@@ -150,197 +190,101 @@ export default Ember.Component.extend(EmberValidations,{
return;
});
},
+ workflowXmlDownload(workflowXml){
+ var link = document.createElement("a");
+ link.download = "workflow.xml";
+ link.href = "data:text/xml,"+vkbeautify.xml(workflowXml);
+ link.click();
+ },
nodeRendered: function(){
- var self=this;
+ this.doValidation();
if(this.get('renderNodeTransitions')){
- var connections=[];
- var visitedNodes=[];
- this.renderTransitions(this.get("workflow").startNode,connections,visitedNodes);
- this.workflowConnections=connections;
+ this.flowRenderer.onDidUpdate(this,this.get("workflow").startNode,this.get("workflow"));
this.layout();
- this.designerPlumb.setSuspendDrawing(true);
- this.designerPlumb.batch(function(){
- connections.forEach(function(conn){
- self.designerPlumb.connect(conn);
- });
- });
- this.designerPlumb.setSuspendDrawing(false,true);
this.set('renderNodeTransitions',false);
}
+ this.resize();
this.persistWorkInProgress();
}.on('didUpdate'),
- cleanUpJsplumb:function(){
- this.get('flattenedNodes').clear();
+ resize(){
+ this.flowRenderer.resize();
+ },
+ cleanupFlowRenderer:function(){
this.set('renderNodeTransitions',false);
- this.designerPlumb.detachEveryConnection();
+ this.flowRenderer.cleanup();
}.on('willDestroyElement'),
initAndRenderWorkflow(){
- this.designerPlumb.ready(function() {
+ var panelOffset=this.$(".designer-panel").offset();
+ var canvasHeight=Ember.$(window).height()-panelOffset.top-25;
+ this.flowRenderer.initRenderer(function(){
this.renderWorkflow();
- }.bind(this));
+ }.bind(this),{context:this,flattenedNodes:this.get("flattenedNodes"),dataNodes:this.get("dataNodes"), cyOverflow:this.get("cyOverflow"),canvasHeight:canvasHeight});
},
renderWorkflow(){
- this.get('flattenedNodes').clear();
this.set('renderNodeTransitions', true);
- var visitedNodes=[];
- this.renderNodes(this.get("workflow").startNode,visitedNodes);
+ this.flowRenderer.renderWorkflow(this.get("workflow"));
+ this.doValidation();
},
rerender(){
- this.designerPlumb.detachEveryConnection();
+ this.flowRenderer.cleanup();
this.renderWorkflow(this.get("workflow"));
},
setCurrentTransition(transition){
this.set("currentTransition",transition);
},
- renderNodes(node,visitedNodes){
- if (!node || node.isKillNode()){
- return;
- }
- if (visitedNodes.contains(node)){
- return;
- }
- visitedNodes.push(node);
- if(!this.get('flattenedNodes').contains(node)){
- this.get('flattenedNodes').pushObject(node);
- }
- if (node.transitions.length > 0){
- node.transitions.forEach(function(transition) {
- var target = transition.targetNode;
- this.renderNodes(target,visitedNodes);
- }.bind(this));
- }
- },
- createConnection(sourceNode,target,transition){
- var connectionColor="#777";
- var lineWidth=1;
- if (transition.condition){
- if(transition.condition==="default"){
- lineWidth=2;
- }else if (transition.condition==="error"|| transition.errorPath){
- connectionColor=Constants.globalSetting.errorTransitionColor;
- }
- }
- var connectionObj={
- source:sourceNode.id,
- target:target.id,
- connector:["Straight"],
- paintStyle:{lineWidth:lineWidth,strokeStyle:connectionColor},
- endpointStyle:{fillStyle:'rgb(243,229,0)'},
- endpoint: ["Dot", {
- radius: 1
- }],
- alwaysRespectStubs:true,
- anchors: [["Bottom"],["Top"]],
- overlays:[]
- };
- return connectionObj;
+ actionInfo(node){
+ this.send("showNotification", node);
},
deleteTransition(transition){
+ this.createSnapshot();
this.get("workflow").deleteTransition(transition);
+ this.showUndo('transition');
this.rerender();
},
- renderTransitions(sourceNode,connections,visitedNodes){
+ showWorkflowActionSelect(element){
var self=this;
- if(!sourceNode){
- return;
- }
- if (visitedNodes.contains(sourceNode)){
- return;
- }
- if (sourceNode.hasTransition() ){
- var transitionCount=sourceNode.transitions.length;
- sourceNode.transitions.forEach(function(transition) {
- var target = transition.targetNode;
- if (target.isKillNode() || !Constants.showErrorTransitions && transition.isOnError()){
- return;
- }
- var connectionObj=self.createConnection(sourceNode,target,transition);
-
- if (transition.condition){
- var conditionHTML = "<div class='decision-condition' title='"+transition.condition+"'>"+ transition.condition+"</div>";
- connectionObj.overlays.push([ "Label", {label:conditionHTML, location:0.75, id:"myLabel" } ]);
- }
- if (!target.isPlaceholder()){
- connectionObj.overlays.push(["PlainArrow",{location:-0.1,width: 7,length: 7}]);
- }
- if (!(sourceNode.isPlaceholder() || target.isKillNode())){
- var location=target.type==="placeholder"?1:0.5;
- var addNodeoverlay=["Custom" , {
- id: sourceNode.id+"_"+target.id+"_"+"connector",
- location:location,
- create:function(component) {
- var container=Ember.$('<div />');
- var plus= Ember.$('<div class="fa fa-plus connector_overlay_new"></div>');
- if ((sourceNode.isDecisionNode() && transitionCount>1 ||sourceNode.isForkNode() && transitionCount>2 ) &&
- target.isPlaceholder() &&
- !transition.isDefaultCasePath()){
- var trash=Ember.$('<div class="node_actions node_left"><i class="fa fa-trash-o"></i></div>');
- trash.on("click",function(){
- self.deleteTransition(transition);
- });
- plus.append(trash);
- }
- container.append(plus);
- return container;
- },
- events:{
- click:function(labelOverlay, originalEvent) {
- var element = originalEvent.target;
- self.set('popOverElement', element);
- self.setCurrentTransition(transition);
- self.$('.popover').popover('destroy');
- Ember.$(element).parents(".jsplumb-overlay").css("z-index", "4");
- self.$(element).attr('data-toggle','popover');
- self.$(element).popover({
- html : true,
- title : "Add Node <button type='button' class='close'>×</button>",
- placement: 'right',
- trigger : 'focus',
- content : function(){
- return self.$('#workflow-actions').html();
- }
- });
- self.$(element).popover("show");
- self.$('.popover .close').on('click',function(){
- Ember.$(".jsplumb-overlay").css("z-index", "");
- self.$('.popover').popover('destroy');
- });
- }
- }
- }];
- connectionObj.overlays.push(addNodeoverlay);
- }
- connections.push(connectionObj);
- self.renderTransitions(target,connections,visitedNodes);
- });
- }
+ this.$('.popover').popover('destroy');
+ Ember.$(element).parents(".jsplumb-overlay").css("z-index", "4");
+ this.$(element).attr('data-toggle','popover');
+ this.$(element).popover({
+ html : true,
+ title : "Add Node <button type='button' class='close'>×</button>",
+ placement: 'right',
+ trigger : 'focus',
+ content : function(){
+ return self.$('#workflow-actions').html();
+ }
+ });
+ this.$(element).popover("show");
+ this.$('.popover .close').on('click',function(){
+ Ember.$(".jsplumb-overlay").css("z-index", "");
+ this.$('.popover').popover('destroy');
+ }.bind(this));
},
+
layout(){
- var nodes = Ember.$(".nodecontainer");
- //var edges = this.designerPlumb.getConnections();
- var edges=this.workflowConnections;
- this.layoutManager.doLayout(this,nodes,edges,this.get("workflow"));
- this.designerPlumb.repaintEverything();
- var endNodeTop=this.$("#node-end").offset().top;
- var endNodeLeft=this.$("#node-end").offset().left;
- this.$("#killnodes-container").offset({top:endNodeTop+50,left:endNodeLeft-50});
- var top = this.$("#killnodes-container").offset().top + 40;
- var left = this.$("#killnodes-container").offset().left - 28;
- this.$('.kill').each(function(index,value){
- this.$(value).offset({top:top,left:left});
- top = this.$(value).offset().top+70 ;
- }.bind(this));
+ this.flowRenderer.refresh();
},
doValidation(){
- this.set('validationErrors',[]);
- this.validate().then(() => {
- this.set('validationErrors',[]);
- }).catch(() => {
- this.get('flattenedNodes').filterBy('errors',true).forEach((node)=>{
- this.get('validationErrors').pushObjects(node.errorMsgs);
- }.bind(this));
-
- }.bind(this));
+ this.validate();
+ },
+ getStackTrace(data){
+ if(data){
+ try{
+ var stackTraceMsg = JSON.parse(data).stackTrace;
+ if(!stackTraceMsg){
+ return "";
+ }
+ if(stackTraceMsg instanceof Array){
+ return stackTraceMsg.join("").replace(/\tat /g, ' at ');
+ } else {
+ return stackTraceMsg.replace(/\tat /g, '<br/> at ');
+ }
+ } catch(err){
+ return "";
+ }
+ }
+ return "";
},
importWorkflow(filePath){
var self = this;
@@ -352,17 +296,34 @@ export default Ember.Component.extend(EmberValidations,{
workflowXmlDefered.promise.then(function(data){
this.importWorkflowFromString(data);
this.set("isWorkflowImporting", false);
- }.bind(this)).catch(function(e){
+ }.bind(this)).catch(function(data){
+ var stackTraceMsg = self.getStackTrace(data.responseText);
+ if(stackTraceMsg.length){
+ self.set("isStackTraceVisible", true);
+ self.set("stackTrace", stackTraceMsg);
+ self.set("isStackTraceAvailable", true);
+ } else {
+ self.set("isStackTraceVisible", false);
+ self.set("isStackTraceAvailable", false);
+ }
self.set("isWorkflowImporting", false);
self.set("isImportingSuccess", false);
});
},
importWorkflowFromString(data){
var workflow=this.get("workflowImporter").importWorkflow(data);
- this.resetDesigner();
- this.set("workflow",workflow);
- this.rerender();
- this.doValidation();
+ if(this.get('workflow')){
+ this.resetDesigner();
+ this.set("workflow",workflow);
+ this.initAndRenderWorkflow();
+ this.rerender();
+ this.doValidation();
+ }else{
+ this.workflow.initialize();
+ this.set("workflow",workflow);
+ this.initAndRenderWorkflow();
+ this.$('#wf_title').focus();
+ }
},
getWorkflowFromHdfs(filePath){
var url = Ember.ENV.API_URL + "/readWorkflowXml?workflowXmlPath="+filePath;
@@ -377,16 +338,16 @@ export default Ember.Component.extend(EmberValidations,{
}
}).done(function(data){
deferred.resolve(data);
- }).fail(function(){
- deferred.reject();
+ }).fail(function(data){
+ deferred.reject(data);
});
return deferred;
},
resetDesigner(){
this.set("isImportingSuccess", true);
- this.set("xmlAppPath", null)
- this.set('errors',{});
- this.set('validationErrors',{});
+ this.set("xmlAppPath", null);
+ this.set('errors',[]);
+ this.set('validationErrors',[]);
this.set('workflowFilePath',"");
this.get("workflow").resetWorfklow();
this.set('globalConfig', {});
@@ -395,7 +356,7 @@ export default Ember.Component.extend(EmberValidations,{
this.set('workflow.parameters', {});
}
this.set('parameters', {});
- this.designerPlumb.reset();
+ this.flowRenderer.reset();
},
resetZoomLevel(){
this.set("zoomLevel", 1);
@@ -423,33 +384,150 @@ export default Ember.Component.extend(EmberValidations,{
return deferred;
},
persistWorkInProgress(){
- //TODO later
+ var json=JSON.stringify(this.get("workflow"));
+ this.get('workspaceManager').saveWorkInProgress(this.get('tabInfo.id'), json);
+ },
+ getDraftWorkflow(){
+ var drafWorkflowJson = this.get('workspaceManager').restoreWorkInProgress(this.get('tabInfo.id'));
+ var workflowImporter=WorkflowJsonImporter.create({});
+ var workflow=workflowImporter.importWorkflow(drafWorkflowJson);
+ return workflow;
+ },
+ createSnapshot() {
+ this.set('undoAvailable', false);
+ this.set('workflowSnapshot', JSON.stringify(this.get("workflow")));
+ },
+ showUndo (type){
+ this.set('undoAvailable', true);
+ this.set('undoType', type);
+ },
+ deleteWorkflowNode(node){
+ this.createSnapshot();
+ if(node.isKillNode()){
+ var result=this.get("workflow").deleteKillNode(node);
+ if (result && result.status===false){
+ this.get('validationErrors').pushObject({node : node ,message :result.message});
+ }
+ } else {
+ this.get("workflow").deleteNode(node);
+ }
+ this.rerender();
+ this.doValidation();
+ this.showUndo('node');
+ },
+ addWorkflowBranch(node){
+ this.createSnapshot();
+ this.get("workflow").addBranch(node);
+ this.rerender();
+ },
+ openWorkflowEditor(node){
+ this.createSnapshot();
+ var validOkToNodes = WorkflowPathUtil.findValidTransitionsTo(this.get('workflow'), node);
+ this.set('showActionEditor', true);
+ this.set('currentAction', node.actionType);
+ var domain = node.getNodeDetail();
+ this.set('clonedDomain',Ember.copy(domain));
+ this.set('clonedErrorNode', node.errorNode);
+ this.set('clonedKillMessage',node.get('killMessage'));
+ node.set("domain", domain);
+ node.set("validOkToNodes", validOkToNodes);
+ this.set('currentNode', node);
+ },
+ openDecisionEditor(node) {
+ this.get("addBranchListener").trigger("showBranchOptions", node);
+ },
+
+ copyNode(node){
+ this.get('clipboardService').setContent(node, 'copy');
+ },
+ cutNode(node){
+ this.get('clipboardService').setContent(node, 'cut');
+ this.deleteWorkflowNode(node);
+ },
+ replaceNode(node){
+ var clipboardContent = this.get('clipboardService').getContent();
+ Ember.set(node, 'name', clipboardContent.name+'-copy');
+ Ember.set(node, 'domain', clipboardContent.domain);
+ Ember.set(node, 'actionType', clipboardContent.actionType);
+ this.rerender();
+ this.doValidation();
},
- restoreWorkinProgress(){
- //TODO later
+ scrollToNewPosition(){
+ if (Constants.useCytoscape){
+ return;
+ }
+ var scroll = Ember.$(window).scrollTop();
+ Ember.$('html, body')
+ .animate({
+ scrollTop: scroll+200
+ }, 1000);
+ },
+ openSaveWorkflow (){
+ this.get('workflowContext').clearErrors();
+ var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"),
+ workflowContext:this.get('workflowContext')});
+ var workflowXml=workflowGenerator.process();
+ if(this.get('workflowContext').hasErrors()){
+ this.set('errors',this.get('workflowContext').getErrors());
+ }else{
+ var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(workflowXml);
+ var configForSubmit={props:dynamicProperties,xml:workflowXml,params:this.get('workflow.parameters')};
+ this.set("workflowSubmitConfigs",configForSubmit);
+ this.set("showingSaveWorkflow",true);
+ }
+ },
+ openJobConfig (){
+ this.get('workflowContext').clearErrors();
+ var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"),
+ workflowContext:this.get('workflowContext')});
+ var workflowXml=workflowGenerator.process();
+ if(this.get('workflowContext').hasErrors()){
+ this.set('errors',this.get('workflowContext').getErrors());
+ }else{
+ var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(workflowXml);
+ var configForSubmit={props:dynamicProperties,xml:workflowXml,params:this.get('workflow.parameters')};
+ this.set("workflowSubmitConfigs",configForSubmit);
+ this.set("showingWorkflowConfigProps",true);
+ }
},
actions:{
+ showStackTrace(){
+ this.set("isStackTraceVisible", true);
+ },
+ hideStackTrace(){
+ this.set("isStackTraceVisible", false);
+ },
showWorkflowSla (value) {
this.set('showWorkflowSla', value);
},
showCreateKillNode (value){
- this.set('showCreateKillNode', value);
+ this.set('showKillNodeManager', value);
+ this.set('addKillNodeMode', true);
+ this.set('editMode', false);
+ },
+ showKillNodeManager (value){
+ this.set('showKillNodeManager', value);
+ this.set('addKillNodeMode', false);
+ },
+ closeKillNodeManager(){
+ this.set("showKillNodeManager", false);
},
showVersionSettings(value){
this.set('showVersionSettings', value);
},
- showParameterSettings(value){
+ showingParameterSettings(value){
if(this.get('workflow.parameters') !== null){
this.set('parameters', Ember.copy(this.get('workflow.parameters')));
}else{
- this.set('globalConfig', {});
+ this.set('parameters', {});
}
this.set('showParameterSettings', value);
},
showCredentials(value){
this.set('showCredentials', value);
},
- createKillNode(){
+ createKillNode(killNode){
+ this.set("killNode", killNode);
this.set("createKillnodeError",null);
var existingKillNode=this.get('workflow').get("killNodes").findBy("name",this.get('killNode.name'));
if (existingKillNode){
@@ -460,7 +538,7 @@ export default Ember.Component.extend(EmberValidations,{
this.set("createKillnodeError","The kill node cannot be empty");
return;
}
- this.get("workflow").createKillNode(this.get('killNode.name'),this.get('killNode.message'));
+ this.get("workflow").createKillNode(this.get('killNode.name'),this.get('killNode.killMessage'));
this.set('killNode',{});
this.rerender();
this.layout();
@@ -469,62 +547,89 @@ export default Ember.Component.extend(EmberValidations,{
this.set('showCreateKillNode', false);
},
addNode(type){
+ this.createSnapshot();
var currentTransition=this.get("currentTransition");
- var newNode=this.get("workflow").addNode(currentTransition,type);
- if(currentTransition.targetNode.isPlaceholder()){
- this.designerPlumb.remove(currentTransition.targetNode.id);
- }
+ this.get("workflow").addNode(this.findTransition(this.get("workflow").startNode, currentTransition.sourceNodeId, currentTransition.targetNode.id),type);
this.rerender();
this.doValidation();
- var scroll = $(window).scrollTop();
- Ember.$('html, body')
- .animate({
- scrollTop: scroll+200
- }, 1000);
+ this.scrollToNewPosition();
},
+
nameChanged(){
this.doValidation();
},
- deleteNode(node){
- if(node.isKillNode()){
- var result=this.get("workflow").deleteKillNode(node);
- if (result && result.status===false){
- this.get('validationErrors').pushObject({node : node ,message :result.message});
- }
- } else {
- this.get("workflow").deleteNode(node);
+ copyNode(node){
+ this.copyNode(node);
+ },
+ pasteNode(){
+ var clipboardContent = this.get('clipboardService').getContent();
+ var currentTransition = this.get("currentTransition");
+ var node = this.get("workflow").addNode(currentTransition, clipboardContent.actionType);
+ if(clipboardContent.operation === 'cut'){
+ node.name = clipboardContent.name;
+ }else{
+ node.name = clipboardContent.name + '-copy';
}
+ node.domain = clipboardContent.domain;
+ node.actionType = clipboardContent.actionType;
this.rerender();
this.doValidation();
+ this.scrollToNewPosition();
+ },
+ deleteNode(node){
+ this.deleteWorkflowNode(node);
},
openEditor(node){
- this.set('showActionEditor', true);
- this.set('currentAction', node.actionType);
- var domain = node.getNodeDetail();
- this.set('clonedDomain',Ember.copy(domain));
- this.set('clonedErrorNode', node.errorNode);
- this.set('clonedKillMessage',node.get('killMessage'));
- node.set("domain", domain);
- this.set('currentNode', node);
+ this.openWorkflowEditor(node);
+ },
+ setFilePath(filePath){
+ this.set("workflowFilePath", filePath);
+ },
+ showNotification(node){
+ this.set("showNotificationPanel", true);
+ if(node.actionType){
+ //this.set("hoveredWidget", node.actionType+"-action-info");
+ //this.set("hoveredAction", node.getNodeDetail());
+ }
+ },
+ hideNotification(){
+ this.set("showNotificationPanel", false);
},
addBranch(node){
- this.get("workflow").addBranch(node);
- this.rerender();
+ this.addWorkflowBranch(node);
},
addDecisionBranch(settings){
+ this.createSnapshot();
this.get("workflow").addDecisionBranch(settings);
this.rerender();
},
- addKillNode(errorNode){
+ setNodeTransitions(transition){
var currentNode= this.get("currentNode");
- if(errorNode && errorNode.isNew){
- this.get("workflow").addKillNode(currentNode,errorNode);
- this.get("workflow.killNodes").push(errorNode);
+ if(transition.errorNode && transition.errorNode.isNew){
+ this.get("workflow").addKillNode(currentNode,transition.errorNode);
+ this.get("workflow.killNodes").push(transition.errorNode);
}else {
- this.set('currentNode.errorNode', errorNode);
+ this.set('currentNode.errorNode', transition.errorNode);
}
+ currentNode.transitions.forEach((trans)=>{
+ if(transition.okToNode){
+ if(trans.targetNode.id !== transition.okToNode.id){
+ trans.targetNode = transition.okToNode;
+ this.showUndo('transition');
+ }
+ }
+ }, this);
},
submitWorkflow(){
+ this.set('dryrun', false);
+ this.openJobConfig();
+ },
+ saveWorkflow(){
+ this.set('dryrun', false);
+ this.openSaveWorkflow();
+ },
+ previewWorkflow(){
+ this.set("showingPreview",false);
this.get('workflowContext').clearErrors();
var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"),
workflowContext:this.get('workflowContext')});
@@ -532,15 +637,11 @@ export default Ember.Component.extend(EmberValidations,{
if(this.get('workflowContext').hasErrors()){
this.set('errors',this.get('workflowContext').getErrors());
}else{
- var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(workflowXml);
- var configForSubmit={props:dynamicProperties,xml:workflowXml,params:this.get('workflow.parameters')};
- this.set("workflowSubmitConfigs",configForSubmit);
- this.set("showingWorkflowConfigProps",true);
+ this.set("previewXml",vkbeautify.xml(workflowXml));
+ this.set("showingPreview",true);
}
-
},
- previewWorkflow(){
- this.set("showingPreview",false);
+ downloadWorkflowXml(){
this.get('workflowContext').clearErrors();
var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"),
workflowContext:this.get('workflowContext')});
@@ -548,12 +649,15 @@ export default Ember.Component.extend(EmberValidations,{
if(this.get('workflowContext').hasErrors()){
this.set('errors',this.get('workflowContext').getErrors());
}else{
- this.set("previewXml",vkbeautify.xml(workflowXml));
- this.set("showingPreview",true);
+ this.workflowXmlDownload(workflowXml);
}
},
closeWorkflowSubmitConfigs(){
this.set("showingWorkflowConfigProps",false);
+ this.set("showingSaveWorkflow",false);
+ },
+ closeSaveWorkflow(){
+ this.set("showingSaveWorkflow",false);
},
importWorkflowTest(){
var deferred = this.importSampleWorkflow();
@@ -563,6 +667,7 @@ export default Ember.Component.extend(EmberValidations,{
this.rerender();
this.doValidation();
}.bind(this)).catch(function(e){
+ console.error(e);
});
},
closeFileBrowser(){
@@ -625,7 +730,11 @@ export default Ember.Component.extend(EmberValidations,{
this.resetZoomLevel();
this.$("#flow-designer").css("transform", "scale(" + 1 + ")");
},
+ resetLayout() {
+ this.flowRenderer.resetLayout();
+ },
closeActionEditor (isSaved){
+ this.send("hideNotification");
if(isSaved){
this.currentNode.onSave();
this.doValidation();
@@ -638,6 +747,27 @@ export default Ember.Component.extend(EmberValidations,{
}
this.set('showActionEditor', false);
this.rerender();
+ },
+ saveDraft(){
+ this.persistWorkInProgress();
+ },
+
+ undoDelete () {
+ var workflowImporter = WorkflowJsonImporter.create({});
+ var workflow = workflowImporter.importWorkflow(this.get('workflowSnapshot'));
+ this.resetDesigner();
+ this.set("workflow", workflow);
+ this.rerender();
+ this.doValidation();
+ this.set('undoAvailable', false);
+ },
+
+ registerAddBranchAction(component){
+ this.set("addBranchListener",component);
+ },
+ dryRunWorkflow(){
+ this.set('dryrun', true);
+ this.openJobConfig();
}
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js
new file mode 100644
index 0000000..73cabac
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js
@@ -0,0 +1,26 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+import Ember from 'ember';
+export default Ember.Component.extend({
+ actions : {
+ hideNotification(){
+ this.sendAction("hideNotification");
+ }
+ }
+});
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js
index 98a292d..03acbf3 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js
@@ -15,11 +15,19 @@
* limitations under the License.
*/
import Ember from 'ember';
-import EmberValidations, {
- validator
-} from 'ember-validations';
+import { validator, buildValidations } from 'ember-cp-validations';
-export default Ember.Component.extend(EmberValidations, {
+const Validations = buildValidations({
+ 'actionModel.fsOps': {
+ validators: [
+ validator('fs-action-validator', {
+ dependentKeys: ['actionModel.fsOps.@each']
+ })
+ ]
+ }
+});
+
+export default Ember.Component.extend(Validations, {
fileBrowser: Ember.inject.service('file-browser'),
setUp: function() {
if (this.get('actionModel.fsOps') === undefined) {
@@ -29,6 +37,8 @@ export default Ember.Component.extend(EmberValidations, {
this.set("actionModel.configuration", {});
this.set("actionModel.configuration.property", Ember.A([]));
}
+ var field = 'validations.attrs.actionModel.fsOps.isDirty';
+ this.set(field, false);
this.sendAction('register', 'fsAction', this);
}.on('init'),
initialize: function() {
@@ -41,52 +51,6 @@ export default Ember.Component.extend(EmberValidations, {
this.$('#collapseOne').collapse('show');
}
}.on('didUpdate'),
- validations: {
- 'actionModel': {
- inline: validator(function() {
- var isValidated = true,
- msg = "";
- if (!this.get('actionModel.fsOps')) {
- return;
- }
- this.get('actionModel.fsOps').forEach(function(item, index) {
- switch (item.type) {
- case "mkdir":
- case "delete":
- case "touchz":
- if (!item.settings.path) {
- isValidated = false;
- msg = "path is mandatory";
- }
- break;
- case "chmod":
- if (!item.settings.path) {
- isValidated = false;
- msg = "path and permissions are mandatory";
- }
- break;
- case "chgrp":
- if (!item.settings.path || !item.settings.group) {
- isValidated = false;
- msg = "path and group are mandatory";
- }
- break;
- case "move":
- if (!item.settings.source || !item.settings.target) {
- isValidated = false;
- msg = "source and target are mandatory";
- }
- break;
- }
- });
- if (!isValidated) {
- return " ";
- }
-
- })
- }
- },
-
actions: {
openFileBrowser(model, context) {
if (undefined === context) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js
new file mode 100644
index 0000000..caf23b4
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js
index fc5fd37..c9be41a 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js
@@ -57,7 +57,7 @@ export default Ember.Component.extend({
},
createFolder(){
var self=this;
- var $elem=this.$("#selectedPath");
+ var $elem=this.$('input[name="selectedPath"]');
//$elem.val($elem.val()+"/");
var folderHint="<enter folder here>";
this.set("selectedPath",this.get("selectedPath")+"/"+folderHint);
@@ -98,7 +98,8 @@ export default Ember.Component.extend({
this.showNotification({
"type": "error",
"message": "Upload Failed",
- "details":textStatus
+ "details":textStatus,
+ "errorThrown":errorThrown
});
},
uploadProgress(e){
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js
new file mode 100644
index 0000000..73cabac
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js
@@ -0,0 +1,26 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+import Ember from 'ember';
+export default Ember.Component.extend({
+ actions : {
+ hideNotification(){
+ this.sendAction("hideNotification");
+ }
+ }
+});
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js
index 125aac3..ac85a9a 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js
@@ -16,9 +16,25 @@
*/
import Ember from 'ember';
-import EmberValidations from 'ember-validations';
+import { validator, buildValidations } from 'ember-cp-validations';
-export default Ember.Component.extend(EmberValidations,{
+const Validations = buildValidations({
+ 'actionModel.script': validator('presence', {
+ presence : true,
+ disabled(model, attribute) {
+ return !model.get('isScript');
+ },
+ dependentKeys : ['isScript']
+ }),
+ 'actionModel.query': validator('presence', {
+ presence : true,
+ disabled(model, attribute) {
+ return model.get('isScript');
+ },
+ dependentKeys : ['isScript']
+ })
+});
+export default Ember.Component.extend(Validations, {
hiveOptionObserver : Ember.observer('isScript',function(){
if(this.get('isScript')){
this.set("actionModel.query", undefined);
@@ -68,20 +84,6 @@ export default Ember.Component.extend(EmberValidations,{
this.$('#collapseOne').collapse('show');
}
}.on('didUpdate'),
- validations : {
- 'actionModel.script': {
- presence: {
- 'if':'isScript',
- 'message' : 'You need to provide a value for Script'
- }
- },
- 'actionModel.query': {
- presence: {
- unless :'isScript',
- 'message' : 'You need to provide a value for Query'
- }
- }
- },
actions : {
openFileBrowser(model, context){
if(undefined === context){
@@ -95,9 +97,9 @@ export default Ember.Component.extend(EmberValidations,{
},
onHiveOptionChange(value){
if(value === "script"){
- this.set('isScript',true);
+ this.set('isScript', true);
}else{
- this.set('isScript',false);
+ this.set('isScript', false);
}
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js
new file mode 100644
index 0000000..3192c72
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js
@@ -0,0 +1,25 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+import Ember from 'ember';
+export default Ember.Component.extend({
+ actions : {
+ hideNotification(){
+ this.sendAction("hideNotification");
+ }
+ }
+});
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js
index f8b53c5..f23cca7 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js
@@ -16,9 +16,30 @@
*/
import Ember from 'ember';
-import EmberValidations from 'ember-validations';
+import { validator, buildValidations } from 'ember-cp-validations';
-export default Ember.Component.extend(EmberValidations,{
+const Validations = buildValidations({
+ 'actionModel.script': validator('presence', {
+ presence : true,
+ disabled(model, attribute) {
+ return !model.get('isScript');
+ },
+ dependentKeys : ['isScript']
+ }),
+ 'actionModel.query': validator('presence', {
+ presence : true,
+ disabled(model, attribute) {
+ return model.get('isScript');
+ },
+ dependentKeys : ['isScript']
+ }),
+ 'actionModel.jdbc-url': validator('presence', {
+ presence : true
+ })
+
+});
+
+export default Ember.Component.extend(Validations,{
hiveOptionObserver : Ember.observer('isScript',function(){
if(this.get('isScript')){
this.set("actionModel.query", undefined);
@@ -68,25 +89,6 @@ export default Ember.Component.extend(EmberValidations,{
this.$('#collapseOne').collapse('show');
}
}.on('didUpdate'),
- validations : {
- 'actionModel.script': {
- presence: {
- 'if':'isScript',
- 'message' : 'You need to provide a value for Script'
- }
- },
- 'actionModel.query': {
- presence: {
- unless :'isScript',
- 'message' : 'You need to provide a value for Query'
- }
- },
- 'actionModel.jdbc-url': {
- presence: {
- 'message' : 'You need to provide a value for jdbc url'
- }
- }
- },
actions : {
openFileBrowser(model, context){
if(undefined === context){
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js
new file mode 100644
index 0000000..73cabac
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js
@@ -0,0 +1,26 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+import Ember from 'ember';
+export default Ember.Component.extend({
+ actions : {
+ hideNotification(){
+ this.sendAction("hideNotification");
+ }
+ }
+});
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js
new file mode 100644
index 0000000..c46a37f
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js
@@ -0,0 +1,54 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ multivalued : true,
+ instance : {
+ value : '',
+ displayValue : '',
+ type : 'date'
+ },
+ initialize : function(){
+ this.sendAction('register', this, this);
+ this.on('bindInputPlaceholder',function () {
+ this.set('addUnboundValue', true);
+ }.bind(this));
+ }.on('init'),
+ bindInputPlaceholder : function () {
+ if(this.get('addUnboundValue') && !Ember.isBlank(this.get('instance'))){
+ this.addinstance();
+ }
+ }.on('willDestroyElement'),
+ addInstance (){
+ this.get('instances').pushObject(Ember.copy(this.get('instance')));
+ this.set('instance', {
+ value : '',
+ displayValue : '',
+ type : 'date'
+ });
+ },
+ actions : {
+ addInstance () {
+ this.addInstance();
+ },
+ deleteInstance (index) {
+ this.get('instances').removeAt(index);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js
new file mode 100644
index 0000000..3192c72
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js
@@ -0,0 +1,25 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+import Ember from 'ember';
+export default Ember.Component.extend({
+ actions : {
+ hideNotification(){
+ this.sendAction("hideNotification");
+ }
+ }
+});
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js
index 873c284..9d43fe7 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js
@@ -16,9 +16,18 @@
*/
import Ember from 'ember';
-import EmberValidations from 'ember-validations';
+import { validator, buildValidations } from 'ember-cp-validations';
-export default Ember.Component.extend(EmberValidations, {
+const Validations = buildValidations({
+ 'actionModel.mainClass': validator('presence', {
+ presence : true
+ }),
+ 'actionModel.jobTracker': validator('presence', {
+ presence : true
+ })
+});
+
+export default Ember.Component.extend(Validations, {
fileBrowser : Ember.inject.service('file-browser'),
javaOptsObserver : Ember.observer('isSingle',function(){
if(this.get('isSingle')){
@@ -67,18 +76,6 @@ export default Ember.Component.extend(EmberValidations, {
this.$('#collapseOne').collapse('show');
}
}.on('didUpdate'),
- validations : {
- 'actionModel.mainClass': {
- presence: {
- 'message' : 'You need to provide a value for Main Class',
- },
- format: {
- with: /([a-z][a-z_0-9]*\.)*[A-Za-z_]($[A-Za-z_]|[\w_])*/,
- allowBlank: false,
- message: 'You need to provide a valid value'
- }
- }
- },
actions : {
openFileBrowser(model, context){
if(undefined === context){
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js
new file mode 100644
index 0000000..4fa2666
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js
@@ -0,0 +1,303 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+import Ember from 'ember';
+import Constants from '../utils/constants';
+import { validator, buildValidations } from 'ember-cp-validations';
+
+const Validations = buildValidations({
+ 'filePath': validator('presence', {
+ presence : true
+ }),
+ 'configMap': {
+ validators: [
+ validator('job-params-validator', {
+ dependentKeys: ['configMap.@each.value', 'showErrorMessage']
+ })
+ ]
+ }
+});
+
+
+export default Ember.Component.extend(Validations, {
+ systemConfigs : Ember.A([]),
+ showingFileBrowser : false,
+ jobXml : "",
+ overwritePath : false,
+ configMap : Ember.A([]),
+ configPropsExists : false,
+ savingInProgress : false,
+ isStackTraceVisible: false,
+ isStackTraceAvailable: false,
+ alertType : "",
+ alertMessage : "",
+ alertDetails : "",
+ filePath : "",
+ showErrorMessage: false,
+ displayName : Ember.computed('type', function(){
+ if(this.get('type') === 'wf'){
+ return "Workflow";
+ }else if(this.get('type') === 'coord'){
+ return "Coordinator";
+ }else{
+ return "Bundle";
+ }
+ }),
+ initialize :function(){
+ this.set("configPropsExists", this.get("jobConfigs").props.size>0);
+ var configProperties = [];
+ configProperties.pushObjects(this.extractJobParams());
+ configProperties.pushObjects(this.extractJobProperties());
+ this.configureExecutionSettings();
+ this.set("configMap", configProperties);
+ this.set("jobXml", this.get("jobConfigs").xml);
+ this.set('filePath', Ember.copy(this.get('jobFilePath')));
+ Object.keys(this.get('validations.attrs')).forEach((attr)=>{
+ var field = 'validations.attrs.'+attr+'.isDirty';
+ this.set(field, false);
+ }, this);
+ }.on('init'),
+ rendered : function(){
+ this.$("#configureJob").on('hidden.bs.modal', function () {
+ this.sendAction('closeJobConfigs');
+ }.bind(this));
+ this.$("#configureJob").modal("show");
+ }.on('didInsertElement'),
+ extractJobParams(){
+ var params = [];
+ var jobParams = this.get("jobConfigs").params;
+ if(jobParams && jobParams.configuration && jobParams.configuration.property){
+ jobParams.configuration.property.forEach((param)=>{
+ if(param && !param.value){
+ var prop= Ember.Object.create({
+ name: param.name,
+ value: null,
+ isRequired : true
+ });
+ params.push(prop);
+ }
+ });
+ }
+ return params;
+ },
+
+ extractJobProperties(){
+ var jobProperties = [];
+ var jobParams = this.get("jobConfigs").params;
+ this.get("jobConfigs").props.forEach(function(value) {
+ if (value!== Constants.defaultNameNodeValue && value!==Constants.rmDefaultValue){
+ var propName = value.trim().substring(2, value.length-1);
+ var isRequired = true;
+ if(jobParams && jobParams.configuration && jobParams.configuration.property){
+ var param = jobParams.configuration.property.findBy('name', propName);
+ if(param && param.value){
+ isRequired = false;
+ }else {
+ isRequired = true;
+ }
+ }
+ var prop= Ember.Object.create({
+ name: propName,
+ value: null,
+ isRequired : isRequired
+ });
+ jobProperties.push(prop);
+ }
+ });
+ return jobProperties;
+ },
+ configureExecutionSettings (){
+ this.set('systemConfigs', Ember.A([]));
+ if(this.get('type') !== 'coord' && !this.get('isDryrun')){
+ this.get('systemConfigs').pushObject({displayName: 'Run on submit', name : 'runOnSubmit', value: false});
+ }
+ this.get('systemConfigs').pushObjects([
+ {displayName: 'Use system lib path', name :'useSystemLibPath', value:true},
+ {displayName: 'Rerun on Failure', name : 'rerunOnFailure', value:true}
+ ]);
+ },
+ showNotification(data){
+ if (!data){
+ return;
+ }
+ if (data.type === "success"){
+ this.set("alertType", "success");
+ }
+ if (data.type === "error"){
+ this.set("alertType", "danger");
+ }
+ this.set("alertDetails", data.details);
+ this.set("alertMessage", data.message);
+ if(data.stackTrace.length){
+ this.set("stackTrace", data.stackTrace);
+ this.set("isStackTraceAvailable", true);
+ } else {
+ this.set("isStackTraceAvailable", false);
+ }
+ },
+ prepareJobForSubmission(isDryrun){
+ if(this.get('validations.isInvalid')){
+ return;
+ };
+ this.set('jobFilePath', Ember.copy(this.get('filePath')));
+ var url = Ember.ENV.API_URL + "/submitJob?app.path=" + this.get("filePath") + "&overwrite=" + this.get("overwritePath");
+ url = url + "&jobType=" + this.get('displayName').toUpperCase();
+ var submitConfigs = this.get("configMap");
+ submitConfigs.forEach(function(item) {
+ url = url + "&config." + item.name + "=" + item.value;
+ }, this);
+ this.get('systemConfigs').forEach((config)=>{
+ if(config.name === 'runOnSubmit' && !isDryrun){
+ url = url + "&oozieparam.action=start";
+ }else if(config.name !== 'runOnSubmit'){
+ url = url + "&oozieconfig." + config.name + "=" + config.value;
+ }
+ });
+ if(isDryrun){
+ url = url + "&oozieparam.action=dryrun";
+ }
+ if ( this.get("jobConfigs").props.has("${resourceManager}")){
+ url= url + "&resourceManager=useDefault";
+ }
+ this.set("savingInProgress", true);
+ this.submitJob(url);
+ },
+ submitJob(url){
+ Ember.$.ajax({
+ url: url,
+ method: "POST",
+ dataType: "text",
+ contentType: "text/plain;charset=utf-8",
+ beforeSend: function(request) {
+ request.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+ request.setRequestHeader("X-Requested-By", "workflow-designer");
+ },
+ data: this.get("jobXml"),
+ success: function(response) {
+ var result=JSON.parse(response);
+ this.showNotification({
+ "type": "success",
+ "message": this.get('displayName') +" saved.",
+ "details": "Job id :"+result.id
+ });
+ this.set("savingInProgress",false);
+ }.bind(this),
+ error: function(response) {
+ console.log(response);
+ this.set("savingInProgress",false);
+ this.set("isStackTraceVisible",true);
+ this.showNotification({
+ "type": "error",
+ "message": "Error occurred while saving "+ this.get('displayName').toLowerCase(),
+ "details": this.getParsedErrorResponse(response),
+ "stackTrace": this.getStackTrace(response.responseText)
+ });
+ }.bind(this)
+ });
+ },
+ getStackTrace(data){
+ if(data){
+ try{
+ var stackTraceMsg = JSON.parse(data).stackTrace;
+ if(!stackTraceMsg){
+ return "";
+ }
+ if(stackTraceMsg instanceof Array){
+ return stackTraceMsg.join("").replace(/\tat /g, '<br/> at ');
+ } else {
+ return stackTraceMsg.replace(/\tat /g, '<br/> at ');
+ }
+ } catch(err){
+ return "";
+ }
+ }
+ return "";
+ },
+ startJob (jobId){
+ this.set('startingInProgress', true);
+ var url = [Ember.ENV.API_URL,
+ "/v2/job/", jobId, "?action=", 'start','&user.name=oozie'
+ ].join("");
+ Ember.$.ajax({
+ url: url,
+ method: 'PUT',
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+ xhr.setRequestHeader("X-Requested-By", "Ambari");
+ }
+ }).done(function(){
+ this.set('startingInProgress', false);
+ this.showNotification({
+ "type": "success",
+ "message": this.get('displayName')+" Started",
+ "details": jobId
+ });
+ }.bind(this)).fail(function(response){
+ this.set('startingInProgress', false);
+ this.showNotification({
+ "type": "error",
+ "message": "Error occurred while starting "+ this.get('displayName').toLowerCase(),
+ "details": this.getParsedErrorResponse(response),
+ "stackTrace": this.getStackTrace(response.responseText)
+ });
+ }.bind(this));
+ },
+ getParsedErrorResponse (response){
+ var detail;
+ if (response.responseText && response.responseText.charAt(0)==="{"){
+ var jsonResp=JSON.parse(response.responseText);
+ if (jsonResp.status==="workflow.oozie.error"){
+ detail="Oozie error. Please check the workflow.";
+ }else if(jsonResp.message && jsonResp.message.indexOf("<html>") > -1){
+ detail= "";
+ }else{
+ detail=jsonResp.message;
+ }
+ }else{
+ detail=response;
+ }
+ return detail;
+ },
+ actions: {
+ selectFile(){
+ this.set("showingFileBrowser",true);
+ },
+ showStackTrace(){
+ this.set("isStackTraceVisible", true);
+ },
+ hideStackTrace(){
+ this.set("isStackTraceVisible", false);
+ },
+ closeFileBrowser(){
+ this.set("showingFileBrowser",false);
+ },
+ dryrun(){
+ this.set('showErrorMessage', true);
+ this.prepareJobForSubmission(true);
+ },
+ save(){
+ this.set('showErrorMessage', true);
+ this.prepareJobForSubmission(false);
+ },
+ previewXml(){
+ this.set("showingPreview",true);
+ },
+ closePreview(){
+ this.set("showingPreview",false);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js
index ce78e59..e403dc4 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js
@@ -16,8 +16,12 @@
*/
import Ember from 'ember';
+import {WorkflowImporter} from '../domain/workflow-importer';
+import {ActionTypeResolver} from "../domain/action-type-resolver";
export default Ember.Component.extend({
+ workflowImporter: WorkflowImporter.create({}),
+ actionTypeResolver: ActionTypeResolver.create({}),
error : {},
errorMessage : Ember.computed('error', function() {
if(this.get('error').status === 400){
@@ -57,85 +61,342 @@ export default Ember.Component.extend({
}),
initialize : function(){
if(this.get('currentTab')){
- this.$('.nav-tabs a[href="'+this.get('currentTab').attr("href")+'"]').tab('show');
+ this.$('.nav-tabs a[href="'+this.get('currentTab').attr("href")+'"]').click();
if(this.get('model.actions')){
this.set('model.actionDetails', this.get('model.actions')[0]);
}
}
+
+ var x2js = new X2JS();
+ var configurationObj = x2js.xml_str2json(this.get('model.conf'));
+ this.set('model.configurationProperties', configurationObj.configuration.property);
+
this.$('.nav-tabs').on('shown.bs.tab', function(event){
this.sendAction('onTabChange', this.$(event.target));
}.bind(this));
}.on('didInsertElement'),
- actions : {
- back (){
- this.sendAction('back');
- },
- close : function(){
- this.sendAction('close');
- },
- doRefresh : function(){
- this.sendAction('doRefresh');
- },
- getJobDefinition : function () {
- Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=definition&timezone=GMT',function(response){
- this.set('model.jobDefinition', (new XMLSerializer()).serializeToString(response).trim());
- }.bind(this)).fail(function(error){
- this.set('error',error);
- }.bind(this));
- },
- showFirstActionDetail : function(){
- this.set('model.actionDetails', this.get('model.actions')[0]);
- },
- getJobLog : function (params){
- var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=log';
- if(params && params.logFilter){
- url = url + '&logfilter=' + params.logFilter;
+
+ getShape(nodeType) {
+ switch(nodeType) {
+ case 'start' :
+ case 'end' :
+ case 'kill' :
+ case 'fork' :
+ case 'join' :
+ return 'ellipse';
+ case 'action' :
+ return 'roundrectangle';
+ case 'decision' :
+ return 'diamond';
+ default :
+ return 'star';
+ }
+ },
+
+ getNodeActionByName(workflowActions, nodeName) {
+ if (Ember.isArray(workflowActions)) {
+ var actionInfo = workflowActions.filter(function (modelAction) {
+ return modelAction.name === nodeName;
+ });
+ return actionInfo.length>0 ? actionInfo[0] : null;
+ } else {
+ return workflowActions;
+ }
+ },
+
+ getActionStatus(nodeName, nodeType) {
+ if (nodeType === 'start') {
+ nodeName = ':start:';
+ }
+ var nodeAction = this.getNodeActionByName(this.get('model.actions'), nodeName);
+
+ if (nodeAction) {
+ return nodeAction.status;
+ }
+ return "Not-Visited";
+ },
+
+ getNodeActionType(workflowXmlString, nodeType, nodeName) {
+ if (nodeType !== 'action') {
+ return nodeType;
+ }
+
+ var workflowJson = new X2JS().xml_str2json(workflowXmlString);
+
+ if (Ember.isArray(workflowJson['workflow-app'].action)) {
+ var workflowActionJson = workflowJson['workflow-app'].action.filter(function (workflowAction) {
+ return workflowAction._name === nodeName;
+ });
+ if (workflowActionJson.length>0) {
+ return this.actionTypeResolver.getActionType(workflowActionJson[0]);
}
- if(params && params.logActionList){
- url = url + '&type=action&scope='+ params.logActionList;
+ } else {
+ return this.actionTypeResolver.getActionType(workflowJson['workflow-app'].action);
+ }
+ return '';
+ },
+
+ getBgColorBasedOnStatus(nodeStatus) {
+ switch(nodeStatus) {
+ case 'Not-Visited' :
+ return '#ffffff';
+ default :
+ return '#e6e6e6';
+ }
+ },
+
+ getFontColorBasedOnStatus(nodeStatus) {
+ switch(nodeStatus) {
+ case 'ERROR' :
+ case 'KILLED' :
+ case 'FAILED' :
+ return '#a94442';
+ default :
+ return '#262626';
+ }
+ },
+
+ getBorderColorBasedOnStatus(nodeStatus) {
+ switch(nodeStatus) {
+ case 'OK' :
+ return '#5bb75b';
+ case 'ERROR' :
+ case 'KILLED' :
+ case 'FAILED' :
+ return '#a94442';
+ default :
+ return '#808080';
+ }
+ },
+
+ getNodeLabelContent(nodeType) {
+ switch(nodeType) {
+ case 'fork' :
+ return '\uf0e8';
+ case 'join' :
+ return '\uf0e8';
+ default :
+ return '';
+ }
+ },
+
+ getCyDataNodes(workflow){
+ var dataNodes = [];
+ var self=this;
+ workflow.nodeVisitor.process(workflow.startNode, function(node) {
+ if (node.type === 'kill') {
+ return;
}
- Ember.$.get(url,function(response){
- this.set('model.jobLog', response);
- }.bind(this)).fail(function(error){
- this.set('error', error);
- }.bind(this));
+ var nodeActionStatus = self.getActionStatus(node.name, node.type);
+ dataNodes.push({ data:
+ { id: node.id, name: node.name, type: node.type,
+ content: node.name + self.getNodeLabelContent(node.type),
+ shape: self.getShape(node.type),
+ bgColor: self.getBgColorBasedOnStatus(nodeActionStatus),
+ fontColor: self.getFontColorBasedOnStatus(nodeActionStatus),
+ borderColor: self.getBorderColorBasedOnStatus(nodeActionStatus) }
+ });
+ if (node.transitions.length > 0) {
+ node.transitions.forEach(function(tran){
+ if (tran.targetNode.type === 'kill') {
+ return;
+ }
+ dataNodes.push(
+ {
+ data: {
+ id: tran.sourceNodeId + '_to_' + tran.targetNode.id,
+ source:tran.sourceNodeId,
+ target: tran.targetNode.id,
+ borderColor: (self.getActionStatus(tran.targetNode.name, tran.targetNode.type) === 'Not-Visited')
+ ? '#808080' : self.getBorderColorBasedOnStatus(nodeActionStatus)
+ }
+ }
+ );
+ });
+ }
+ });
+ return dataNodes;
},
- getErrorLog : function (){
- Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=errorlog',function(response){
- this.set('model.errorLog', response);
- }.bind(this)).fail(function(error){
- this.set('error', error);
- }.bind(this));
+ showActionNodeDetail(node, workflowXmlString){
+ var nodeName = node.data().name;
+ if (nodeName === 'Start') {
+ nodeName = ':start:';
+ }
+ this.set('model.nodeName', nodeName);
+ this.set('model.nodeType', this.getNodeActionType(workflowXmlString, node.data().type, nodeName));
+ this.set('model.actionDetails', null);
+ var actionInfo = this.getNodeActionByName(this.get('model.actions'), nodeName);
+ this.set('model.actionInfo', actionInfo);
},
- getAuditLog : function (){
- Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=auditlog',function(response){
- this.set('model.auditLog', response);
- }.bind(this)).fail(function(error){
- this.set('error', error);
+ renderDag(xmlString){
+ var workflow = this.get("workflowImporter").importWorkflow(xmlString);
+ console.log("Workflow Object..", workflow);
+ var dataNodes=this.getCyDataNodes(workflow);
+ var cy = cytoscape({
+ container: document.getElementById('cy'),
+ elements: dataNodes,
+ style: [
+ {
+ selector: 'node',
+ style: {
+ shape: 'data(shape)',
+ 'color': 'data(fontColor)',
+ 'background-color': 'data(bgColor)',
+ 'border-width': 1,
+ 'border-color': 'data(borderColor)',
+ label: 'data(name)',
+ 'text-valign': 'center',
+ 'font-size': 8
+ }
+ },
+ {
+ selector: 'node[shape = "roundrectangle"]',
+ style: {
+ width: 100,
+ 'border-radius': 1,
+ }
+ },
+ {
+ selector: 'node[type = "fork"]',
+ style: {
+ 'background-image': 'assets/sitemap.png',
+ 'text-halign': 'left'
+ }
+ },
+ {
+ selector: 'node[type = "join"]',
+ style: {
+ 'background-image': 'assets/join.png',
+ 'text-halign': 'left'
+ }
+ },
+ {
+ selector: 'edge',
+ style: {
+ width: 1,
+ 'line-color': 'data(borderColor)',
+ 'curve-style': 'bezier',
+ 'target-arrow-shape': 'triangle',
+ 'target-arrow-color': 'data(borderColor)'
+ }
+ }
+ ],
+ layout: {
+ name: 'dagre'
+ }
+ });
+
+ // the default values of each option are outlined below:
+ var defaults = {
+ zoomFactor: 2.0, // zoom factor per zoom tick
+ minZoom: 0.1, // min zoom level
+ maxZoom: 10, // max zoom level
+
+ // icon class names
+ sliderHandleIcon: 'fa fa-minus',
+ zoomInIcon: 'fa fa-plus',
+ zoomOutIcon: 'fa fa-minus',
+ resetIcon: 'fa fa-expand'
+ };
+
+ cy.panzoom( defaults );
+
+ cy.on('click', 'node', function(event) {
+ var node = event.cyTarget;
+ this.showActionNodeDetail(node, xmlString);
}.bind(this));
},
- getJobDag : function (){
- this.set('model.jobDag', Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=graph');
+ importSampleWorkflow (){
+ var self=this;
+ Ember.$.ajax({
+ url: "/sampledata/workflow.xml",
+ dataType: "text",
+ cache:false,
+ success: function(data) {
+ self.renderDag(data);
+ }.bind(this),
+ failure : function(data){
+ console.error(data);
+ }
+ });
},
- getCoordActionReruns : function () {
- var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=allruns&type=action';
- if(this.get('rerunActionList')){
- url = url + '&scope=' + this.get('rerunActionList');
+ actions : {
+ back (){
+ this.sendAction('back');
+ },
+ close : function(){
+ this.sendAction('close');
+ },
+ doRefresh : function(){
+ this.sendAction('doRefresh');
+ },
+ getJobDefinition : function () {
+ Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=definition&timezone=GMT',function(response){
+ this.set('model.jobDefinition', (new XMLSerializer()).serializeToString(response).trim());
+ }.bind(this)).fail(function(error){
+ this.set('error',error);
+ }.bind(this));
+ },
+ showFirstActionDetail : function(){
+ this.set('model.actionDetails', this.get('model.actions')[0]);
+ },
+ getJobLog : function (params){
+ var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=log';
+ if(params && params.logFilter){
+ url = url + '&logfilter=' + params.logFilter;
+ }
+ if(params && params.logActionList){
+ url = url + '&type=action&scope='+ params.logActionList;
+ }
+ Ember.$.get(url,function(response){
+ this.set('model.jobLog', response);
+ }.bind(this)).fail(function(error){
+ this.set('error', error);
+ }.bind(this));
+ },
+ getErrorLog : function (){
+ Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=errorlog',function(response){
+ this.set('model.errorLog', response);
+ }.bind(this)).fail(function(error){
+ this.set('error', error);
+ }.bind(this));
+ },
+ getAuditLog : function (){
+ Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=auditlog',function(response){
+ this.set('model.auditLog', response);
+ }.bind(this)).fail(function(error){
+ this.set('error', error);
+ }.bind(this));
+ },
+ getJobDag : function (){
+ //if (true) return this.importSampleWorkflow();
+ Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=definition&timezone=GMT',function(response){
+ var xmlString = (new XMLSerializer()).serializeToString(response).trim();
+ this.renderDag(xmlString);
+ }.bind(this)).fail(function(error){
+ this.set('error',error);
+ }.bind(this));
+ // this.set('model.jobDag', Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=graph');
+ },
+ getCoordActionReruns : function () {
+ var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=allruns&type=action';
+ if(this.get('rerunActionList')){
+ url = url + '&scope=' + this.get('rerunActionList');
+ }
+ Ember.$.get(url, function(response){
+ this.set('model.coordActionReruns', response.workflows);
+ }.bind(this)).fail(function(error){
+ this.set('error', error);
+ }.bind(this));
+ },
+ getActionDetails : function (actionInfo) {
+ this.set('model.actionDetails', actionInfo);
+ },
+ showWorkflow : function(workflowId){
+ this.sendAction('showWorkflow', workflowId);
+ },
+ showCoord : function(coordId){
+ this.sendAction('showCoord', coordId);
}
- Ember.$.get(url, function(response){
- this.set('model.coordActionReruns', response.workflows);
- }.bind(this)).fail(function(error){
- this.set('error', error);
- }.bind(this));
- },
- getActionDetails : function (actionInfo) {
- this.set('model.actionDetails', actionInfo);
- },
- showWorkflow : function(workflowId){
- this.sendAction('showWorkflow', workflowId);
- },
- showCoord : function(coordId){
- this.sendAction('showCoord', coordId);
}
- }
-});
+ });
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js
new file mode 100644
index 0000000..cc9eb1e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js
@@ -0,0 +1,29 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ actions: {
+ createKillNode() {
+ this.sendAction('createKillNode');
+ },
+
+ updateNode() {
+ this.sendAction('updateNode');
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js
new file mode 100644
index 0000000..02660be
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js
@@ -0,0 +1,62 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ initialize : function() {
+ this.set('killNode', {});
+ this.set('editMode', false);
+ }.on('init'),
+
+ rendered : function(){
+ this.$('#killnode-manager-dialog').modal({
+ backdrop: 'static',
+ keyboard: false
+ });
+ this.$('#killnode-manager-dialog').modal('show');
+ this.$('#killnode-manager-dialog').modal().on('hidden.bs.modal', function() {
+ this.sendAction('closeKillNodeManager');
+ }.bind(this));
+ }.on('didInsertElement'),
+ actions: {
+ deleteNode(index) {
+ this.get('killNodes').removeAt(index);
+ this.set('editMode', false);
+ },
+ addNode() {
+ this.set('createKillnodeError',null);
+ this.set('editMode', false);
+ this.set('addKillNodeMode', true);
+ this.set('killNode', {});
+ },
+ editNode(index) {
+ this.set('createKillnodeError', null);
+ this.set('editMode', true);
+ this.set('currentKillNodeIndex', index);
+ var selectedKillNode = this.get('killNodes').objectAt(index);
+ this.set('killNode', {name : selectedKillNode.name, killMessage: selectedKillNode.killMessage});
+ },
+ updateNode(){
+ this.set('editMode', false);
+ this.get('killNodes').objectAt(this.get('currentKillNodeIndex')).set('killMessage', this.get('killNode').killMessage);
+ this.set('killNode', {});
+ },
+ createKillNode() {
+ this.sendAction('createKillNode', this.get("killNode"));
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js
index c3782a3..3eb8545 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js
@@ -16,9 +16,8 @@
*/
import Ember from 'ember';
-import EmberValidations from 'ember-validations';
-export default Ember.Component.extend(EmberValidations, {
+export default Ember.Component.extend({
hasStaticProps : true,
fileBrowser : Ember.inject.service('file-browser'),
staticProps : Ember.A([]),
@@ -65,9 +64,6 @@ export default Ember.Component.extend(EmberValidations, {
}.bind(this));
this.sendAction('register','mapRedAction', this);
}.on('didInsertElement'),
- validations : {
-
- },
observeError :function(){
if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
this.$('#collapseOne').collapse('show');