You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2016/08/09 20:15:01 UTC
[08/12] ambari git commit: AMBARI-17213. Create ambari workflow
designer contrib view. (Venkat Ranganathan via yusaku)
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
new file mode 100644
index 0000000..f29adb6
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
@@ -0,0 +1,134 @@
+/*
+* 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 CommonUtils from "../utils/common-utils";
+import {Workflow} from '../domain/workflow';
+import {WorkflowXmlMapper} from '../domain/workflow_xml_mapper';
+var WorkflowImporter= Ember.Object.extend({
+ workflowMapper:null,
+ x2js : new X2JS(),
+ importWorkflow(workflowXml){
+ var workflow=Workflow.create({});
+ workflow.initialize();
+ this.workflowMapper=WorkflowXmlMapper.create({schemaVersions:workflow.schemaVersions});
+ return this.processWorkflowXml(workflowXml,workflow);
+ },
+ processWorkflowXml(workflowXml,workflow){
+ var workflowJson= this.get("x2js").xml_str2json(workflowXml);
+ if (!workflowJson["workflow-app"]){
+ throw "Invalid workflow";
+ }
+ var workflowAppJson=workflowJson["workflow-app"];
+ var workflowVersion=CommonUtils.extractSchemaVersion(workflowAppJson._xmlns);
+ workflow.schemaVersions.setCurrentWorkflowVersion(workflowVersion);
+ if (workflowAppJson.info && workflowAppJson.info.__prefix==="sla") {
+ workflow.slaEnabled=true;
+ this.workflowMapper.handleSLAImport(workflow,workflowAppJson.info);
+ }
+ this.workflowMapper.handleCredentialImport(workflow,workflowAppJson.credentials);
+ this.workflowMapper.handleParametersImport(workflow,workflowAppJson.parameters);
+ var nodeMap=this.setupNodeMap(workflowAppJson,workflow);
+ this.setupTransitions(workflowAppJson,nodeMap);
+ workflow.set("startNode",nodeMap.get("start").node);
+ this.populateKillNodes(workflow,nodeMap);
+ return workflow;
+ },
+ processActionNode(nodeMap,action){
+ var actionMapper=this.get("workflowMapper").getNodeHandler("action");
+ var actionNode=actionMapper.handleImportNode(action);
+ nodeMap.set(actionNode.getName(),actionNode);
+ },
+ setupNodeMap(workflowAppJson,workflow){
+ var self=this;
+ workflow.set("name",workflowAppJson["_name"]);
+ var nodeMap=new Map();
+ Object.keys(workflowAppJson).forEach(function (key) {
+ var nodeHandler=self.workflowMapper.getNodeHandler(key);
+ if (nodeHandler){
+ if (Ember.isArray(workflowAppJson[key])){
+ workflowAppJson[key].forEach(function(jsonObj){
+ var node=nodeHandler.handleImportNode(key,jsonObj,workflow);
+ nodeMap.set(jsonObj._name,{json:jsonObj,node:node});
+ });
+ }else{
+ var node=nodeHandler.handleImportNode(key,workflowAppJson[key],workflow);
+ if (!workflowAppJson[key]._name){
+ nodeMap.set(key,{json:workflowAppJson[key],node:node});
+ }else{
+ nodeMap.set(workflowAppJson[key]._name,{json:workflowAppJson[key],node:node});
+ }
+ }
+ }
+ });
+ return nodeMap;
+ },
+ setupTransitions(workflowAppJson,nodeMap){
+ var self=this;
+ nodeMap.forEach(function(entry,key){
+ var node=entry.node;
+ if (!node){
+ console.error("could not process:",key);//TODO error handling...
+ return;
+ }
+ var json=entry.json;
+ var nodeHandler=self.workflowMapper.getNodeHandler(node.get("type"));
+ if (!nodeHandler){
+ console.error("could not process:",node.get("type"));//TODO error handling...
+ }
+ nodeHandler.handleImportTransitions(node,json,nodeMap);
+ });
+ },
+ getNodeIds(nodeMap){
+ var ids=[];
+ nodeMap.forEach(function(entry,key){
+ var node=entry.node;
+ ids.push(node.id);
+ });
+ return ids;
+ },
+ getNodeNames(nodeMap){
+ var names=[];
+ nodeMap.forEach(function(entry,key){
+ var node=entry.node;
+ names.push(node.id);
+ });
+ return names;
+ },
+ populateKillNodes(workflow,nodeMap){
+ if (this.containsKillNode(nodeMap)){
+ workflow.resetKillNodes();
+ }
+ nodeMap.forEach(function(entry,key){
+ var node=entry.node;
+ if (node.isKillNode()){
+ workflow.get("killNodes").pushObject(node);
+ }
+ });
+ },
+ containsKillNode(nodeMap){
+ var containsKillNode=false;
+ nodeMap.forEach(function(entry,key){
+ var node=entry.node;
+ if (node.isKillNode()){
+ containsKillNode=true;
+ }
+ });
+ return containsKillNode;
+ }
+});
+export {WorkflowImporter};
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
new file mode 100644
index 0000000..9fc791c
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
@@ -0,0 +1,144 @@
+/*
+ * 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 {WorkflowXmlMapper} from '../domain/workflow_xml_mapper';
+import {NodeVisitor} from '../domain/node-visitor';
+import Constants from '../utils/constants';
+var WorkflowGenerator= Ember.Object.extend({
+ workflowMapper:null,
+ x2js : new X2JS({useDoubleQuotes:true}),
+ workflow:null,
+ workflowContext : {},
+ nodeVisitor:null,
+ ignoreErrors:false,
+ init(){
+ this.workflowMapper=WorkflowXmlMapper.create({schemaVersions:this.workflow.schemaVersions});
+ this.nodeVisitor=NodeVisitor.create({});
+ },
+ process(){
+ if (!this.ignoreErrors && (!this.workflow.get("name") || this.workflow.get("name").trim()==="")){
+ this.workflowContext.addError({message : "Workflow name is mandatory"});
+ return;
+ }
+ var workflowObj={"workflow-app":{}};
+ this.get("workflowMapper").getGlobalConfigHandler().handle(this.workflow.get("globalSetting"),workflowObj["workflow-app"]);
+ this.visitNode(workflowObj,this.workflow.startNode);
+ if (this.workflow.slaEnabled===true){
+ this.get("workflowMapper").handleSLAMapping(this.workflow.sla,workflowObj["workflow-app"]);
+ }
+ this.get("workflowMapper").handleCredentialsGeneration(this.workflow.credentials,workflowObj["workflow-app"]);
+ this.get("workflowMapper").hanldeParametersGeneration(this.workflow.parameters,workflowObj["workflow-app"]);
+ if (!this.ignoreErrors && (!workflowObj["workflow-app"].action || workflowObj["workflow-app"].action.length<1)){
+ this.workflowContext.addError({message : "Miniumum of one action node must exist"});
+ return;
+ }
+ var reordered={"workflow-app":{}};
+ var srcWorkflowApp=workflowObj["workflow-app"];
+ var targetWorkflowApp=reordered["workflow-app"];
+ targetWorkflowApp["_name"]=this.workflow.get("name");
+ this.copyProp(srcWorkflowApp,targetWorkflowApp,["parameters","global","credentials","start","decision","fork","join","action","kill","end","info"]);
+ targetWorkflowApp["_xmlns"]="uri:oozie:workflow:"+this.workflow.get("schemaVersions").getCurrentWorkflowVersion();
+ if (this.slaInfoExists(targetWorkflowApp)){
+ targetWorkflowApp["_xmlns:sla"]="uri:oozie:sla:0.2";
+ }
+ var xmlAsStr = this.get("x2js").json2xml_str(reordered);
+ return xmlAsStr;
+ },
+ slaInfoExists(workflowApp){
+ if (workflowApp.info){
+ return true;
+ }
+ var slaExists= false;
+ if (!workflowApp.action){
+ return false;
+ }
+ workflowApp.action.forEach(function(action){
+ if (action.info){
+ slaExists=true;
+ }
+ });
+ return slaExists;
+ },
+ copyProp(src,dest,props){
+ props.forEach(function(prop){
+ if (src[prop]){
+ dest[prop]=src[prop];
+ }
+ });
+ },
+
+ visitNode(workflowObj,node,visitedNodes){
+ if (!visitedNodes){
+ visitedNodes=[];
+ }
+ if (visitedNodes.contains(node.get("id"))){
+ return;
+ }
+ visitedNodes.push(node.get("id"));
+ var self=this;
+ var workflowApp=workflowObj["workflow-app"];
+ if (node.isPlaceholder()){
+ return self.visitNode(workflowObj,node.transitions[0].targetNode,visitedNodes);
+ }
+ var nodeHandler=this.get("workflowMapper").getNodeHandler(node.type);
+ nodeHandler.setContext(this.workflowContext);
+ var nodeObj=nodeHandler.handleNode(node);
+ if (node.type==='action'){
+ var jobHandler=this.get("workflowMapper").getActionJobHandler(node.actionType);
+ if (jobHandler){
+
+ jobHandler.setContext(this.workflowContext);
+ if (!self.ignoreErrors && !node.get("domain")){
+ this.workflowContext.addError({node : node, message : "Action Properties are empty"});
+ }else{
+ jobHandler.handle(node.get("domain"),nodeObj,node.get("name"));
+ if (!self.ignoreErrors){
+ var errors=jobHandler.validate(node.get("domain"));
+ if (errors && errors.length>0){
+ errors.forEach(function(err){
+ this.workflowContext.addError({node : node, message : err.message});
+ }.bind(this));
+ }
+ }
+ }
+ }else{
+ if (!self.ignoreErrors){
+ this.workflowContext.addError({node : node, message : "Unknown action:"+node.actionType});
+ }
+ }
+ }
+ if (nodeHandler.hasMany()){
+ if (!workflowApp[node.type]){
+ workflowApp[node.type]=[];
+ }
+ workflowApp[node.type].push(nodeObj);
+ }else{
+ workflowApp[node.type]=nodeObj;
+ }
+ nodeHandler.handleTransitions(node.transitions,nodeObj);
+ if (node.transitions){
+ node.transitions.forEach(function(tran){
+ self.visitNode(workflowObj,tran.targetNode,visitedNodes);
+ });
+ }
+ if (node.isActionNode()){
+ nodeHandler.handleSla(node.domain,nodeObj);
+ }
+ }
+});
+export {WorkflowGenerator};
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
new file mode 100644
index 0000000..5908de5
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
@@ -0,0 +1,266 @@
+/*
+* 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 {FindNodeMixin} from '../domain/findnode-mixin';
+import {NodeFactory} from '../domain/node-factory';
+import SchemaVersions from '../domain/schema-versions';
+import {NodeVisitor} from '../domain/node-visitor';
+import {idGen} from '../domain/id-gen';
+import {SlaInfo} from '../domain/sla-info'
+var Workflow= Ember.Object.extend(FindNodeMixin,{
+ name:"",
+ startNode:null,
+ globalSetting:null,
+ parameters: null,
+ usePlaceholders: true,
+ killNodes : null,
+ nodeVisitor : null,
+ nodeFactory:NodeFactory.create({}),
+ schemaVersions:SchemaVersions.create({}),
+ sla : SlaInfo.create({}),
+ credentials : Ember.A([]),
+ initialize(){
+ this.nodeVisitor=NodeVisitor.create({});
+ var src =this.nodeFactory.createStartNode();
+ var dest =this.nodeFactory.createEndNode("end");
+ this.set("startNode", src);
+ this.set("killNodes",Ember.A([]));
+ this.set("globalSetting",null);
+ this.set("name","");
+ this.appendDefaultKillNode();
+ src.addTransitionTo(dest);
+ },
+ appendDefaultKillNode(){
+ this.createKillNode(Constants.defaultKillNodeName,"${wf:errorMessage(wf:lastErrorNode())}");
+ },
+ createKillNode(name, message){
+ var killNode=this.nodeFactory.createKillNode(name,message);
+ this.get("killNodes").pushObject(killNode);
+ },
+ resetKillNodes(){
+ this.set("killNodes",Ember.A([]));
+ },
+ resetWorfklow(){
+ //TODO idGen.reset();
+ this.initialize();
+ },
+ findCommonTargetNodeId(node){
+ var nodeIds = {}, targ, decPath = node.getTargets(), tempId = 0;
+ for(var i =0; i< decPath.length; i++){
+ var currNode = decPath[i];
+ do {
+ if(nodeIds.hasOwnProperty(currNode.get("id"))){
+ nodeIds[currNode.get("id")] = nodeIds[currNode.get("id")] + 1;
+ } else {
+ nodeIds[currNode.get("id")] = 1;
+ }
+ if(currNode.get("id") === "node-end"){
+ break;
+ }
+ currNode = currNode.getTargets()[0];
+ } while(currNode && currNode.get("id"));
+ }
+ for(var j in nodeIds){
+ if(tempId < nodeIds[j]){
+ targ = j;
+ tempId = nodeIds[j];
+ }
+ }
+ return targ;
+ },
+ findJoinNode(node){
+ var commonTargetId=null;
+ var commonTarget=null;
+ if (node.isDecisionNode()){
+ if (Constants.globalSetting.useJoinNodeForDecision){
+ var target=this.findNodeById(node,"decision_end_"+node.get("id"));
+ if (!target){
+ commonTargetId=this.findCommonTargetNodeId(node);
+ commonTarget=this.findNodeById(this.startNode,commonTargetId);
+ return commonTarget;
+ }else{
+ return target;
+ }
+ }else{
+ commonTargetId=this.findCommonTargetNodeId(node);
+ commonTarget=this.findNodeById(this.startNode,commonTargetId);
+ return commonTarget;
+ }
+ }else if (node.isForkNode()) {
+ commonTargetId=this.findCommonTargetNodeId(node);
+ commonTarget=this.findNodeById(this.startNode,commonTargetId);
+ return commonTarget;
+ }else{
+ return null;
+ }
+ },
+ addBranch(sourceNode){
+ var target=this.findJoinNode(sourceNode);
+ if (this.get("usePlaceholders")){
+ var placeholderNode=this.nodeFactory.createPlaceholderNode(target) ;
+ sourceNode.addTransitionTo(placeholderNode);
+ }else{
+ sourceNode.addTransitionTo(target);
+ }
+ },
+ addDecisionBranch(settings){
+ if (!settings.targetNode){
+ return;
+ }
+ var sourceNode=settings.sourceNode;
+ var insertNodeOnPath=settings.newNodeType?true:false;
+ var target=settings.targetNode;
+ if (!insertNodeOnPath){
+ if (this.get("usePlaceholders")){
+ var placeholderNode=this.nodeFactory.createPlaceholderNode(target) ;
+ sourceNode.addTransitionTo(placeholderNode,settings.condition);
+ }else{
+ sourceNode.addTransitionTo(target,settings.condition);
+ }
+ }else{
+ }
+ },
+ generatedNode(target,type){
+ var generatedNode=null;
+ if ("decision" === type){
+ generatedNode=this.nodeFactory.generateDecisionNode(target);
+ }else if ("fork" === type){
+ generatedNode=this.nodeFactory.generateForkNode(target);
+ }else if ("kill" === type){
+ generatedNode = this.nodeFactory.createKillNode(settings.name);
+ source.deleteCurrentKillNode();
+ }else{
+ generatedNode = this.nodeFactory.createActionNode(type);
+ generatedNode.addTransitionTo(target);
+ }
+ return generatedNode;
+ },
+ addKillNode(node,settings){
+ var generatedNode=this.generatedNode(null,"kill");
+ return source.addTransitionTo(generatedNode,"error");
+ },
+ addNode(transition,type,settings) {
+ var source=transition.sourceNode;
+ var target=transition.targetNode;
+ var computedTarget=target;
+ if (target && target.isPlaceholder()){
+ computedTarget=target.getTargets()[0];
+ }
+ var generatedNode=this.generatedNode(computedTarget,type);
+ transition.targetNode=generatedNode;
+ return generatedNode;
+ },
+ deleteKillNode(node){
+ let killNodes = this.get("killNodes");
+ var killNodeReferenced=false;
+ this.nodeVisitor.process(this.startNode,function(n,ctx){
+ if (n.errorNode && n.errorNode.name===node.name){
+ killNodeReferenced=true;
+ }
+ });
+ if (killNodeReferenced){
+ return{
+ status: false,
+ message: "Kill node is being referenced by other nodes."
+ };
+ }
+ for(var i=0; i<killNodes.length; i++){
+ if(node.id === killNodes[i].id){
+ this.get("killNodes").removeObject(killNodes[i]);
+ break;
+ }
+ }
+ return {
+ status:true
+ };
+ },
+ deleteNode(node){
+ var self=this;
+ var target=node.getDefaultTransitionTarget();
+ if (node.isForkNode()|| node.isDecisionNode()){
+ target=this.findJoinNode(node);
+ if (target.isJoinNode()){
+ target=target.getDefaultTransitionTarget();
+ }
+ }
+ var transitionslist=this.findTransistionsToNode(node);
+ transitionslist.forEach(function(tran){
+ if (tran.getSourceNode().isDecisionNode()){
+ var joinNode=self.findJoinNode(tran.getSourceNode());
+ if (joinNode===target){
+ if (tran.isDefaultCasePath()){
+ var placeholderNode=self.nodeFactory.createPlaceholderNode(target);
+ tran.targetNode=placeholderNode;
+ }else if (tran.getSourceNode().getOkTransitionCount()>2){
+ tran.getSourceNode().removeTransition(tran);
+ }else{
+ var placeholderNode=self.nodeFactory.createPlaceholderNode(target);
+ tran.targetNode=placeholderNode;
+ }
+ }else{
+ tran.targetNode=target;
+ }
+ }else if (tran.getSourceNode().isForkNode()){
+ var joinNode=self.findJoinNode(tran.getSourceNode());
+ if (joinNode===target){
+ if (tran.getSourceNode().getOkTransitionCount()>2){
+ tran.getSourceNode().removeTransition(tran);
+ }else{
+ var placeholderNode=self.nodeFactory.createPlaceholderNode(target);
+ tran.targetNode=placeholderNode;
+ }
+ }else{
+ tran.targetNode=target;
+ }
+ }else{
+ tran.targetNode=target;
+ }
+ });
+ },
+ deleteTransition(transition){
+ var src=transition.getSourceNode();
+ src.removeTransition(transition);
+ },
+ deleteEmptyTransitions(transitionslist){
+ transitionslist.forEach(function(tran){
+ if (tran.getSourceNode().isForkNode()&& tran.getTargetNode().isJoinNode()){
+ tran.getSourceNode().removeTransition(tran);
+ }
+ });
+ },
+ findTransistionsToNode(matchingNode){
+ var transitionslist=[];
+ this.findTransistionsToNodeInternal(this.startNode,matchingNode,transitionslist);
+ return transitionslist;
+ },
+ findTransistionsToNodeInternal(node,matchingNode,transitionslist){
+ var self=this;
+ if (node.transitions){
+ node.transitions.forEach(function(tran){
+ if (tran.getTargetNode()===matchingNode){
+ transitionslist.push(tran);
+ }
+ self.findTransistionsToNodeInternal(tran.getTargetNode(),matchingNode,transitionslist);
+ });
+ }
+ }
+});
+
+
+export {Workflow};
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
new file mode 100644
index 0000000..d5dc4da
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
@@ -0,0 +1,135 @@
+/*
+* 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 * as nodeHandler from '../domain/node-handler';
+import {SLAMapper} from "../domain/mapping-utils";
+
+import {MappingMixin,ConfigurationMapper} from "../domain/mapping-utils";
+var WorkflowXmlMapper= Ember.Object.extend({
+ nodeHandlerMap:null,
+ globalConfigHandler:null,
+ actionTypeResolver:null,
+ slaMapper: SLAMapper.create({}),
+ schemaVersions:null,
+ init: function() {
+ this.actionTypeResolver=nodeHandler.ActionTypeResolver.create({schemaVersions:this.schemaVersions});
+ this.set("globalConfigHandler",GlobalConfigHandler.create({}));
+ this.set("slaMapper",SLAMapper.create({}));
+ this.nodeHandlerMap=new Map();
+ this.nodeHandlerMap.set("start",nodeHandler.StartNodeHandler.create({}));
+ this.nodeHandlerMap.set("end",nodeHandler.EndNodeHandler.create({}));
+ this.nodeHandlerMap.set("action",nodeHandler.ActionNodeHandler.create({actionTypeResolver:this.actionTypeResolver}));
+ this.nodeHandlerMap.set("decision",nodeHandler.DecisionNodeHandler.create({}));
+ this.nodeHandlerMap.set("fork",nodeHandler.ForkNodeHandler.create({}));
+ this.nodeHandlerMap.set("join",nodeHandler.JoinNodeHandler.create({}));
+ this.nodeHandlerMap.set("kill",nodeHandler.KillNodeHandler.create({}));
+ },
+ getNodeHandler(type){
+ return this.nodeHandlerMap.get(type);
+ },
+ getGlobalConfigHandler(){
+ return this.globalConfigHandler;
+ },
+ getActionJobHandler(jobType){
+ return this.actionTypeResolver.getActionJobHandler(jobType);
+ },
+ handleSLAMapping(sla,workflowObj){
+ this.get("slaMapper").hanldeGeneration(sla,workflowObj);
+ },
+ handleSLAImport(workflow,infoJson){
+ this.slaMapper.handleImport(workflow,infoJson,"sla");
+ },
+ handleCredentialsGeneration(credentials,workflowObj){
+ if (credentials && credentials.length>0){
+ workflowObj["credentials"]={"credential":[]};
+ credentials.forEach(function(credential){
+ var credJson={_name:credential.name,_type:credential.type,property:[]};
+ if (credential.property){
+ credential.property.forEach(function(prop){
+ credJson.property.push({"name":prop.name,"value":prop.value});
+ });
+ }
+ workflowObj.credentials.credential.push(credJson);
+ });
+ }
+ },
+ handleCredentialImport(workflow,credJson){
+ if (!credJson || !credJson.credential){
+ return;
+ }
+ workflow.credentials.clear();
+ var credntialsJson=Ember.isArray(credJson.credential)?credJson.credential:[credJson.credential];
+ credntialsJson.forEach(function(cred){
+ var credential={
+ name:cred._name,
+ type:cred._type,
+ property:Ember.A([])
+ };
+ if (cred.property){
+ if (!Ember.isArray(cred.property)){
+ cred.property=[cred.property];
+ }
+ cred.property.forEach(function(property){
+ credential.property.push({"name":property.name,"value":property.value});
+ });
+ }
+ workflow.credentials.push(credential);
+ });
+ },
+ hanldeParametersGeneration(parameters,workflowObj){
+ if (!parameters || !parameters.configuration){
+ return;
+ }
+ if (parameters.configuration.property.length>0){
+ workflowObj.parameters={property:[]};
+ }
+ parameters.configuration.property.forEach(function(prop){
+ workflowObj.parameters.property.push({"name":prop.name,"value":prop.value});
+ });
+ },
+ handleParametersImport(workflow,parameters){
+ if (!parameters|| !parameters.property){
+ return;
+ }
+ workflow.parameters={"configuration":{property:[]}}
+ parameters.property.forEach(function(prop){
+ workflow.parameters.configuration.property.push({"name":prop.name,"value":prop.value});
+ });
+ }
+});
+var GlobalConfigHandler=Ember.Object.extend(MappingMixin,{
+ mapping:null,
+ configurationMapper:ConfigurationMapper.create({}),
+ init(){
+ this.mapping=[
+ {xml:"job-tracker",domain:"jobTracker"},
+ {xml:"name-node",domain:"nameNode"},
+ {xml:"configuration",customHandler:this.configurationMapper}
+ ];
+ },
+
+ handle(domainObject,nodeObj){
+ if (!domainObject){
+ return;
+ }
+ var globalObj={};
+ nodeObj["global"]=globalObj;
+ this.handleMapping(domainObject,globalObj,this.mapping);
+ }
+});
+export {WorkflowXmlMapper};
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/helpers/date-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/helpers/date-helper.js b/contrib/views/wfmanager/src/main/resources/ui/app/helpers/date-helper.js
new file mode 100644
index 0000000..58303c1
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/helpers/date-helper.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.
+*/
+
+/*global moment:false */
+import Ember from 'ember';
+
+export function dateHelper(dateStr) {
+ var date = dateStr && new Date(dateStr);
+ if (!date || isNaN(date.getTime())) {
+ return "";
+ }
+ return [moment(date).format("MM/DD/YYYY hh:mm A")].join("");
+}
+
+export default Ember.Helper.helper(dateHelper);
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/index.html b/contrib/views/wfmanager/src/main/resources/ui/app/index.html
new file mode 100644
index 0000000..a317a48
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/index.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+
+<!--
+ ~ 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.
+ -->
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>Workflow Designer </title>
+ <link rel="shortcut icon" type="image/icon" href="assets/favicon.ico" />
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1"> {{content-for "head"}}
+ <link rel="stylesheet" href="assets/vendor.css">
+ <link rel="stylesheet" href="assets/oozie-designer.css"> {{content-for "head-footer"}}
+ <style>
+ body{background:#fff;}
+ </style>
+</head>
+
+<body>
+ {{content-for "body"}}
+ <script src="assets/vendor.js"></script>
+ <script src="assets/oozie-designer.js"></script>
+</body>
+<script type="text/javascript">
+var AmbariViewHelper = {
+ isAmbariView: function() {
+ return window.location.href.indexOf("/views/") > 0;
+ },
+ getViewInfo: function() {
+ var tokens = window.location.href.substring(window.location.href.indexOf("/views/") + 7).split("/");
+ var info = {
+ name: tokens[0],
+ version: tokens[1],
+ instance: tokens[2]
+ };
+ info.API_URL = ["/api/v1/views/", info.name, "/versions/", info.version, "/instances/" + info.instance].join("");
+ return info;
+ },
+ loadInstanceInfo: function() {
+ var viewInfo = this.getViewInfo();
+ $.ajax({
+ dataType: "json",
+ url: viewInfo.API_URL,
+ async: false,
+ success: function(result) {
+ Ember.ENV.instanceInfo = result;
+ // Update the API URL pointing to Ambari View Resource
+ Ember.ENV.API_URL = viewInfo.API_URL + "/resources/proxy";
+ Ember.ENV.FILE_API_URL = viewInfo.API_URL + "/resources/proxy/fileServices";
+ }
+ });
+ }
+};
+
+if (AmbariViewHelper.isAmbariView()) {
+ $('body').addClass("ambari-view");
+ AmbariViewHelper.loadInstanceInfo();
+}
+</script>
+
+</html>
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/initializers/init.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/initializers/init.js b/contrib/views/wfmanager/src/main/resources/ui/app/initializers/init.js
new file mode 100644
index 0000000..c089258
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/initializers/init.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.
+ */
+
+export function initialize( /* application */ ) {
+ //application.inject('route', 'oozie', 'service:oozie-api');
+}
+
+export default {
+ name: 'init',
+ initialize
+};
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/resolver.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/resolver.js b/contrib/views/wfmanager/src/main/resources/ui/app/resolver.js
new file mode 100644
index 0000000..d0b3de0
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/resolver.js
@@ -0,0 +1,20 @@
+/*
+ * 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 Resolver from 'ember-resolver';
+
+export default Resolver;
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/router.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/router.js b/contrib/views/wfmanager/src/main/resources/ui/app/router.js
new file mode 100644
index 0000000..1fcd633
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/router.js
@@ -0,0 +1,33 @@
+/*
+* 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 config from './config/environment';
+
+const Router = Ember.Router.extend({
+ location: config.locationType
+});
+
+Router.map(function () {
+ this.route('dashboard');
+ this.route('design');
+ this.route('designtest');
+ this.route('job');
+ this.route('connection-error');
+});
+
+export default Router;
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/routes/connection-error.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/connection-error.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/connection-error.js
new file mode 100644
index 0000000..8ea6f8e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/connection-error.js
@@ -0,0 +1,35 @@
+/*
+ * 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.Route.extend({
+ queryParams: {
+ current : { refreshModel : true}
+ },
+ previousLocation : '',
+ beforeModel (transition){
+ this.set('previousLocation',transition.queryParams.current);
+ },
+ model () {
+ var retryLocation = this.get('previousLocation');
+ if(!retryLocation){
+ retryLocation = '/#/dashboard';
+ }
+ return retryLocation;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
new file mode 100644
index 0000000..d0ef5e0
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
@@ -0,0 +1,146 @@
+/*
+* 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.Route.extend({
+ history: Ember.inject.service(),
+ errorMessage : "Error",
+ queryParams: {
+ jobType: { refreshModel: true },
+ start: { refreshModel: true },
+ filter: { refreshModel: true }
+ },
+ actions: {
+ onShowJobDetails : function(params){
+ this.transitionTo('job',{
+ queryParams : {
+ id : params.id,
+ jobType : params.type,
+ from: null,
+ fromType: null
+ }
+ });
+ },
+ loading: function ( /*transition, originRoute*/ ) {
+ Ember.$("#loading").css("display", "block");
+ var app = this.controllerFor('application');
+ if (app.get('currentRouteName') !== "dashboard") {
+ return true;
+ }
+ return false;
+ },
+ error: function() {
+
+ },
+ doSearch (params){
+ this.get('history').setSearchParams(params);
+ Ember.$("#loading").css("display", "block");
+ this.search(params);
+ }
+ },
+ fetchJobs (url){
+ var deferred = Ember.RSVP.defer();
+ Ember.$.get(url).done(function(res){
+ deferred.resolve(res);
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise;
+ },
+ search(params){
+ params = params || {};
+ var type = params.type || "wf",
+ start = Number(params.start || 1),
+ len = Number(params.len || Ember.ENV.PAGE_SIZE),
+ index = 0,
+ filter = params.filter || "",
+ API_URL = Ember.ENV.API_URL,
+ url = [API_URL,
+ "/v2/jobs?jobtype=", type,
+ "&offset=", start,
+ "&len=", len,
+ "&filter=", filter
+ ].join(""),
+ page = (start - 1) / len + 1;
+ return this.fetchJobs(url).catch(function(){
+ this.controllerFor('dashboard').set('model',{error : "Remote API Failed"});
+ Ember.$("#loading").css("display", "none");
+ }.bind(this)).then(function (res) {
+ if(!res){
+ return;
+ }
+ if (typeof res === "string") {
+ res = JSON.parse(res);
+ }
+ res.jobs = [];
+
+ if (res.workflows) {
+ res.areWfs = true;
+ res.type = "wf";
+ res.workflows.forEach(function (job) {
+ job.type = "wf";
+ res.jobs.push(job);
+ });
+ }
+ if (res.coordinatorjobs) {
+ res.areCoords = true;
+ res.type = "coords";
+ res.coordinatorjobs.forEach(function (job) {
+ job.type = "coordinatorjobs";
+ job.id = job.coordJobId;
+ job.appName = job.coordJobName;
+ res.jobs.push(job);
+ });
+ }
+ if (res.bundlejobs) {
+ res.areBundles = true;
+ res.type = "bundles";
+ res.bundlejobs.forEach(function (job) {
+ job.type = "bundlejobs";
+ job.id = job.bundleJobId;
+ job.appName = job.bundleJobName;
+ res.jobs.push(job);
+ });
+ }
+ res.pageSize = len;
+ res.pages = [];
+
+ while (index++ < (res.total / len)) {
+ res.pages.push({ index: index, active: page === index });
+ }
+
+ res.jobTypeValue = type;
+ res.filterValue = filter;
+ res.pageSize = len;
+ res.totalValue = res.total;
+ res.page = page;
+ res.start = start;
+ res.end = (start + res.jobs.length - 1);
+ res.time = new Date().getTime();
+ this.controllerFor('dashboard').set('model', res);
+ Ember.$("#loading").css("display", "none");
+ return res;
+ }.bind(this));
+ },
+ afterModel: function (model) {
+ Ember.$("#loading").css("display", "none");
+ },
+ model: function (params) {
+
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js
new file mode 100644
index 0000000..cec0e9e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.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.Route.extend({
+xmlAppPath : null,
+beforeModel: function(transition){
+ this.set("xmlAppPath", transition.queryParams.appPath);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/routes/designtest.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/designtest.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/designtest.js
new file mode 100644
index 0000000..052f998
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/designtest.js
@@ -0,0 +1,65 @@
+/*
+ * 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.Route.extend({
+ actions:{
+ showChart() {
+ // jsPlumb.ready(function() {
+ // var a = $("#a");
+ // var b = $("#b");
+ var connectionColor="#777";
+ var lineWidth=1;
+ var stateMachineConnector = {
+ connector:"Bezier",
+ paintStyle:{lineWidth:3,strokeStyle:"#056"},
+ hoverPaintStyle:{strokeStyle:"#dbe300"},
+ endpoint:"Blank",
+ anchor:"Continuous",
+ overlays:[ ["PlainArrow", {location:1, width:15, length:12} ]]
+ };
+
+ var connection=jsPlumb.connect({
+ source:"a",
+ target:"b",
+ //connector:["Flowchart", { stub:1,alwaysRespectStubs :true,cornerRadius: 5 }],
+ // connector:["Straight"],
+ // connector:["StateMachine",{curviness:0}],
+ //connector: ["Bezier"],
+ paintStyle:{lineWidth:lineWidth,strokeStyle:connectionColor},
+ endpointStyle:{fillStyle:'rgb(243,229,0)'},
+ endpoint: ["Dot", {
+ radius: 1
+ }],
+ alwaysRespectStubs:true,
+ anchors: [["Bottom"],["Top"]]
+ // anchors: [["Continuous"],["Continuous"]]
+ },stateMachineConnector);
+
+ connection.addOverlay([ "Label", {label:"<span>hello</span>", location:0.75, id:"myLabel" } ]);
+ // jsPlumb.connect({
+ // source:"b",
+ // target:"a"
+ // }, stateMachineConnector);
+
+ jsPlumb.repaintEverything();
+
+
+
+ }
+}
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/routes/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/index.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/index.js
new file mode 100644
index 0000000..0fbbaa2
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/index.js
@@ -0,0 +1,24 @@
+/*
+ * 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.Route.extend({
+ beforeModel() {
+ this.transitionTo('dashboard');
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/routes/job.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/job.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/job.js
new file mode 100644
index 0000000..d849609
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/job.js
@@ -0,0 +1,84 @@
+/*
+ * 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.Route.extend({
+ history: Ember.inject.service(),
+ queryParams: {
+ jobType: { refreshModel: true },
+ id: { refreshModel: true },
+ from : {refreshModel: true},
+ fromType :{refreshModel : true}
+ },
+ from : null,
+ fromType : null,
+ getJobInfo (url){
+ var deferred = Ember.RSVP.defer();
+ Ember.$.get(url).done(function(res){
+ deferred.resolve(res);
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise;
+ },
+ model : function(params){
+ return this.getJobInfo(Ember.ENV.API_URL+'/v2/job/'+params.id+'?show=info&timezone=GMT&offset=1&len='+Ember.ENV.PAGE_SIZE).catch(function(){
+ return {error : "Remote API Failed"};
+ }).then(function(response){
+ if (typeof response === "string") {
+ response = JSON.parse(response);
+ }
+ response.jobType = params.jobType;
+ return response;
+ });
+ },
+ afterModel : function (model, transition){
+ if(transition.queryParams.from){
+ this.set('from', transition.queryParams.from);
+ this.set('fromType',transition.queryParams.fromType);
+ }else{
+ this.set('from', null);
+ this.set('fromType', null);
+ }
+ },
+ actions : {
+ didTransition (){
+ this.controller.set('from', this.get('from'));
+ this.controller.set('fromType',this.get('fromType'));
+ },
+ onTabChange : function(tab){
+ this.set('currentTab', tab);
+ this.controller.set('currentTab',tab);
+ },
+ backToSearch : function(){
+ var params = this.get('history').getSearchParams();
+ if(null != params){
+ this.transitionTo('dashboard', {
+ queryParams: {
+ jobType: params.type,
+ start: params.start,
+ end: params.end,
+ filter: params.filter
+ }
+ });
+ }else{
+ this.transitionTo('dashboard');
+ }
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/services/file-browser.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/services/file-browser.js b/contrib/views/wfmanager/src/main/resources/ui/app/services/file-browser.js
new file mode 100644
index 0000000..d5d8eb9
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/services/file-browser.js
@@ -0,0 +1,27 @@
+/*
+* 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.Service.extend(Ember.Evented,{
+ setContext : function(context){
+ this.set('currentContext', context);
+ },
+ getContext : function(){
+ return this.get('currentContext');
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/services/history.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/services/history.js b/contrib/views/wfmanager/src/main/resources/ui/app/services/history.js
new file mode 100644
index 0000000..f99f1da
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/services/history.js
@@ -0,0 +1,31 @@
+/*
+* 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.Service.extend({
+
+ lastSearch: null,
+
+ getSearchParams: function() {
+ return this.get('lastSearch');
+ },
+
+ setSearchParams : function(routeParams) {
+ this.set('lastSearch', routeParams);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/services/property-extractor.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/services/property-extractor.js b/contrib/views/wfmanager/src/main/resources/ui/app/services/property-extractor.js
new file mode 100644
index 0000000..17ff9aa
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/services/property-extractor.js
@@ -0,0 +1,61 @@
+/*
+* 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.Service.extend({
+ simpleProperty : /^[A-Za-z_][0-9A-Za-z_]+$/,
+ dynamicProperty : /^\${[A-Za-z_][0-9A-Za-z_]*}$/,
+ dynamicPropertyWithElMethod : /^\${[A-Za-z_][0-9A-Za-z_]*\((([A-Za-z_][0-9A-Za-z_]*)(,([A-Za-z_][0-9A-Za-z_]*))*)*\)}$/,
+ dynamicPropertyWithWfMethod : /^\${wf:[A-Za-z_][0-9A-Za-z_]*\((([A-Za-z_][0-9A-Za-z_]*)(,([A-Za-z_][0-9A-Za-z_]*))*)*\)}$/i,
+ hadoopEL : /^\${hadoop:.*}$/,
+ extractor : /\${.+?\}/g,
+ extract : function(property) {
+ var matches = property.match(this.get('extractor'));
+ var dynamicProperties = [];
+ if(null == matches) {
+ return dynamicProperties;
+ }
+ matches.forEach((match)=>{
+ if(this.get('dynamicProperty').test(match)){
+ dynamicProperties.push(match);
+ }
+ }.bind(this));
+ return dynamicProperties;
+ },
+ getProperties : function(currentValue, dynamicProperties) {
+ if( typeof currentValue === "object" ) {
+ Object.keys(currentValue).forEach((value, key)=> {
+ this.getProperties(currentValue[value], dynamicProperties);
+ }.bind(this));
+ } else {
+ if (typeof currentValue==="string"){
+ var extractedProperties = this.extract(currentValue);
+ extractedProperties.forEach((value)=>{
+ dynamicProperties.set(value,value);
+ });
+ }
+ }
+ return dynamicProperties;
+ },
+ getDynamicProperties : function(xml){
+ var x2js = new X2JS();
+ var workflowJSON = x2js.xml_str2json(xml);
+ var props= this.getProperties(workflowJSON, new Map());
+ return props;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.css
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.css b/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.css
new file mode 100644
index 0000000..48232f5
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.css
@@ -0,0 +1,1335 @@
+/*
+ * 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.
+ */
+
+body {
+ background: #eee;
+ padding: 0;
+ margin: 0;
+}
+.padding0 {
+ padding: 0px;
+}
+.paddingtop7{
+ padding-top:7px;
+}
+.paddingtop10{
+ padding-top:10px;
+}
+.padding10{
+ margin-top: 10px;
+}
+.padding20{
+ margin-top: 20px;
+}
+.marginleft20{
+ margin-left: 20px !important;
+}
+#arrow{
+ top: 24%;
+ left: -22px;
+ margin-top: -11px;
+ border-right-color: #999 !important;
+ border-right-color: rgba(0, 0, 0, .25);
+ border-left-width: 0;
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+ border-width: 11px;
+}
+
+#cntnr,
+#container {
+ background: #ddd;
+}
+.disabled {
+ cursor:none;
+}
+#header {
+ white-space: nowrap;
+ background: #5bb75b;
+ background-position: 10px;
+ color: yellow;
+ height: 50px;
+ padding: 10px;
+ font-size: 20px;
+}
+.branch{
+ padding: 0px !important;
+ border: 0px !important;
+ background: inherit !important;
+}
+#sidebar .panel-body {
+ box-shadow: #bbb 3px 0 10px;
+}
+.condJoin {
+}
+.dummyAction{
+ margin:5%;
+}
+
+.branch input {
+ border: 0px;
+ text-align: center;
+}
+#sidebar .panel-body,
+#sidebar .panel-body,
+#content .designer-panel {
+}
+
+.designer-canvas {
+}
+
+#designer {
+ position: relative;
+}
+
+#wf_title {
+ border-bottom: 1px dotted #000;
+}
+
+.editable {
+ padding: 5px 10px;
+ border: 1px solid transparent;
+ background: transparent;
+ white-space: nowrap;
+ text-align: left;
+ margin: 0 2px;
+}
+
+.editable:focus,
+.editable:hover {
+ border: 1px solid #999;
+ background: #fff;
+ cursor: text;
+}
+
+.canDrag {
+ cursor: move;
+}
+
+.action {
+ float: left;
+ padding: 5px;
+ margin: 2px;
+ height: 60px;
+ line-height: 50px;
+ width: 62px;
+ font-size: small;
+ overflow: hidden;
+ text-align: center;
+}
+
+.node,
+.actions_list_left li:hover {
+ /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#ffffff+0,e5e5e5+100;White+3D */
+ background: #ffffff;
+ /* Old browsers */
+ background: -moz-linear-gradient(top, #ffffff 0%, #e5e5e5 100%);
+ /* FF3.6-15 */
+ background: -webkit-linear-gradient(top, #ffffff 0%, #e5e5e5 100%);
+ /* Chrome10-25,Safari5.1-6 */
+ background: linear-gradient(to bottom, #ffffff 0%, #e5e5e5 100%);
+ /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
+ filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e5e5e5', GradientType=0);
+ /* IE6-9 */
+}
+
+.node {
+ text-align: center;
+ position: absolute;
+ left: 50%;
+ border: 1px solid #bbb;
+ overflow: visible;
+ background: transparent;
+ border: none;
+}
+
+.node-item{
+ background:#e5e5e5;
+ border: 1px solid #ABABAB;
+ z-index:4;
+
+}
+
+.action_node {
+ min-width: 195px;
+}
+
+.node:hover {
+}
+
+.fork,
+.join {
+ border-radius: 70px;
+ width: 40px;
+ height: 40px;
+ white-space: wrap;
+ padding-top: 10px;
+}
+
+.start,
+.end {
+ border-radius: 60px;
+ width: 40px;
+ height: 40px;
+ background: #fff;
+ line-height: 25px;
+ cursor: not-allowed;
+}
+
+.start {
+ top: 20px;
+ padding-left: 7px;
+ padding-top: 6px;
+}
+.kill{
+ border-radius: 5px;
+ min-width: 178px;
+ height: 38px;
+}
+.kill .fa-ban {
+ color: red;
+}
+.hrClass{
+ margin-bottom: 10px;
+ margin-top: 10px;
+}
+
+.kill-name{
+ padding-left: 40px;
+ margin-top: -35px;
+}
+
+.node .editable {
+ padding: 2px 5px;
+ width: 8em;
+ text-align: center;
+}
+
+.node .error{
+ border: 1px solid #a94442;
+}
+
+.connector_overlay {
+ transition: all .3s ease-in;
+ white-space: nowrap;
+ opacity: .8;
+ border: 1px solid #ddd;
+ background: #1a8cff;
+ border-radius: 30px;
+ width: 30px;
+ height: 30px;
+ line-height: 28px;
+ text-align: center;
+ cursor: pointer;
+ transform: scale(0.8);
+
+}
+
+.dataPlacement {
+ color: white;
+}
+.jsplumb-hover .connector_overlay,
+.connector_overlay:hover {
+ opacity: 1;
+}
+
+.node_actions {
+ transition: opacity 1s ease-in;
+ opacity: 0;
+ position: absolute;
+ white-space: nowrap;
+ border: 1px solid #999;
+ background: #E5E5E5;
+ border-radius: 3px;
+ min-width: 20px;
+ height: 28px;
+ cursor: pointer;
+}
+.node_actions i{
+ padding: 5px;
+}
+.node_properties {
+ border-radius: 30px;
+ cursor: pointer;
+}
+.node_properties:hover {
+ cursor: pointer;
+}
+.decision_node .node_properties {
+ margin-top: -16px;
+}
+.decisionNodeError{
+ position: relative;
+ left: 50px;
+ }
+.action-node {
+ border-radius: 5px;
+ height: 38px;
+}
+.dummy {
+ width: 160px;
+ height: 50px;
+ box-shadow: none;
+}
+.node_left .fa-trash-o{
+ color: red;
+}
+.node_left {
+ right: -45px;
+ top: -61%;
+}
+.decision_node .node_left{
+ transform: rotate(315deg) scale(1.4);
+ right: -50px;
+ top: -126%;
+}
+.node_left:hover {
+}
+.nodeEnd{
+ top:15%;
+ position: relative;
+}
+.nodeStart{
+ right:5%;
+ position: relative;
+}
+.node_bottom {
+
+ bottom: 50%;
+ left: 50%;
+ z-index: 5;
+ transform: translate(-50%, -20%) scale(0.8);
+ opacity: 1;
+ border-radius: 3px;
+ width: 40px;
+ height: 24px;
+}
+.fork .node_bottom {
+ top: 80%;
+
+}
+.node_bottom_actions{
+ position: absolute;
+ background: #E5E5E5;
+ white-space: nowrap;
+}
+.node:hover .node_actions {
+ opacity: 1;
+}
+
+.action_node {
+ border-radius: 4px;
+}
+
+input:invalid {
+ border: 1px solid red;
+}
+
+.fa-exclamation-triangle {
+ color: red;
+}
+
+.actions_list_left {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.actions_list_left li.disabled {
+ cursor: not-allowed;
+ background: #eee;
+ color: #ccc;
+}
+
+.actions_list_left li {
+ background: #fff;
+ width: 60px;
+ padding: 10px;
+ border: 1px solid silver;
+ border-radius: 5px;
+ margin: 2px 0;
+ cursor: move;
+ z-index: 1000;
+ display: inline-block;
+ position: relative;
+ text-align: center;
+}
+
+#sidebar .actions_list_left li:before {
+ content: '.. .. .. ..';
+ width: 10px;
+ display: block;
+ line-height: 4px;
+ position: absolute;
+ color: #aaa;
+ left: 5px;
+ top: 5px;
+}
+
+.control_flow_node,
+.control_flow li:hover {
+ /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#fdfeea+0,e8f135+100 */
+ /*background: #fdfeea;*/
+ /* Old browsers */
+ /*background: -moz-linear-gradient(top, #fdfeea 0%, #e8f135 100%);*/
+ /* FF3.6-15 */
+ /*background: -webkit-linear-gradient(top, #fdfeea 0%, #e8f135 100%);*/
+ /* Chrome10-25,Safari5.1-6 */
+ /*background: linear-gradient(to bottom, #fdfeea 0%, #e8f135 100%);*/
+ /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
+ /*filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#fdfeea', endColorstr='#e8f135', GradientType=0);*/
+ /* IE6-9 */
+}
+.decision-condition {
+ white-space: nowrap;
+ width: 10em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border: 1px solid #D0D0D0;
+ border-radius: 3px;
+ background-color: #F5F5F5;
+ padding: 3px;
+ font-size: 11px;
+ opacity: 0.8;
+}
+.decision_node {
+ transform: rotate(45deg) scale(.7);
+ width: 60px;
+ height: 60px;
+ padding: 5px;
+ float: right;
+ margin: 0 2px;
+}
+.decision-data-wrapper{
+ transform: rotate(-45deg) scale(1.3); ;
+}
+.decision_node_data .editable{
+ margin-right: 10px;
+}
+.decision_node_data {
+ padding-right: 31px;
+ float: right;
+ margin: 0 2px;
+}
+.fork_node_data {
+ float: right;
+ margin-top: -15px;
+}
+.fork_node_data .editable {
+ margin-right: 5px;
+}
+.decision_node .content {
+ text-align: center;
+ position: absolute;
+ padding: 0 5px;
+ width: 120px;
+ height: 70px;
+ transform: rotate(-45deg) scale(1.2) translate(-13%, 30%);
+ white-space: nowrap;
+}
+
+.breadcrumb {
+ margin: 0;
+}
+
+.collapse.width {
+ height: auto;
+ -webkit-transition: width 0.35s ease;
+ -moz-transition: width 0.35s ease;
+ -o-transition: width 0.35s ease;
+ transition: width 0.35s ease;
+}
+
+.highlighter-always {
+ opacity: 1;
+ animation-name: highlighter;
+ animation-duration: 1s;
+ animation-iteration-count: infinite;
+ animation-timing-function: ease-out;
+}
+
+@keyframes highlighter {
+ 0% {
+ transform: scale(1)
+ }
+ 50% {
+ transform: scale(1.5)
+ }
+ 100% {
+ transform: scale(1)
+ }
+}
+
+.control_flow li._fork {
+ border-radius: 50px;
+ margin: 4px 8px;
+}
+
+.control_flow li.switch {
+ border-radius: 0px;
+ transform: rotate(45deg) translate(0%, -0%) scale(.9);
+}
+
+.active_drag {
+ box-shadow: 5px 5px 5px #aaa;
+}
+
+.can_be_dropped {
+ color: green;
+ border: 1px solid green;
+}
+
+.can_be_dropped:after {
+ font-family: FontAwesome;
+ content: "\f00c";
+ font-size: 25px;
+ display: block;
+ position: absolute;
+ color: #69be28;
+ top: -20px;
+ left: 20px;
+}
+
+.tooltip-inner {
+ white-space: pre;
+ max-width: none;
+}
+
+.action_node_data,
+.action_node_button {
+ padding: 5px;
+ float: left;
+ margin: 0 2px;
+}
+
+.action_node_button:hover {
+ background: #fff;
+}
+
+.caseLabel {
+ padding: 5px;
+ background: rgba(255, 255, 255, .8);
+ font-size: small;
+ border-radius: 20px;
+ border: 1px solid #ccc;
+ white-space: nowrap;
+ color: blue;
+}
+
+#content,
+#sidebar {
+ padding: 0;
+ margin: 0;
+ float: left;
+ position: relative;
+ transition: left .6s cubic-bezier(0.175, 0.885, 0.320, 1.275);
+}
+
+#sidebar {
+ width: 240px;
+ text-align: center;
+ position: absolute;
+ z-index: 1;
+ top: 89px;
+}
+
+#sidebar:after {
+ content: "\f101";
+ position: absolute;
+ right: -10px;
+ top: 50%;
+ font-family: FontAwesome;
+ background: #aaa;
+ color: #fff;
+ padding: 5px;
+}
+
+.expanded {
+ left: 0px;
+}
+
+.expanded:after {
+ content: "\f100" !important;
+}
+
+.collapsed {
+ left: -215px;
+}
+
+.title-btn,
+.title-btn:hover {
+ border: 0;
+ background: transparent;
+}
+
+.back_button {
+ height: 30px;
+ background-color: #fff;
+ position: relative;
+ margin-left: 13px;
+ border-radius: 5px;
+ border: 1px solid #ccc;
+}
+
+.back_button:before {
+ content: "";
+ position: absolute;
+ top: -1px;
+ left: -13px;
+ border-style: solid;
+ border-width: 15px 15px 15px 0;
+ border-color: transparent #ccc transparent transparent;
+}
+
+.back_button:after {
+ content: "";
+ position: absolute;
+ top: 0px;
+ left: -12px;
+ border-style: solid;
+ border-width: 14px 14px 14px 0;
+ border-color: transparent #fff transparent transparent;
+}
+
+.back_button > a {
+ display: block;
+ text-decoration: none;
+ text-align: center;
+ color: #000;
+ padding: 0 10px 0 5px;
+ line-height: 27px;
+ font-size: smaller;
+}
+
+.ambari-view #header {
+ display: none;
+}
+
+.ambari-view #content {
+ padding: 0;
+}
+
+.ambari-view #sidebar {
+ top: 39px;
+}
+
+.ambari-view body {
+ background: #fff;
+}
+
+.messageDiv {
+ position: absolute;
+ left: 40%;
+ top: 100px;
+ z-index: 1;
+}
+
+.workflow-error {
+ top:50px;
+ left: 75%;
+ /*width:50%;*/
+ z-index: 1;
+}
+
+#action_properties_dialog .modal-body {
+ max-height: 350px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+#action_properties_dialog .container,
+#global_properties_dialog .container {
+ max-width: 180%;
+}
+
+#action_properties_dialog .blank-form {
+ right: 15px;
+ position: relative;
+}
+
+#action_properties_dialog .modal-dialog,
+#workflow_sla_dialog .modal-dialog,
+#global_properties_dialog .modal-dialog {
+ width: 800px;
+}
+#collapseOne{
+ margin: 10px;
+}
+.panel-heading {
+ padding: 4px 15px;
+}
+
+#action_properties_dialog .panel-default > .panel-heading,
+#global_properties_dialog .panel-default > .panel-heading,
+#workflow_sla_dialog .panel-default > .panel-heading,
+#workflow_credentials_dialog .panel-default > .panel-heading,
+#configureWorkfowModal .panel-default > .panel-heading,
+ .whiteLabel, .whiteLabel:hover, .whiteLabel:focus, .whiteLabel:visited, .whiteLabel:active {
+ background-color: #5bb75b;
+ color: #fff;
+}
+.btn-primary{
+ background-color: #5bb75b !important;
+ border-color: #289028 !important;
+ color: #fff;
+}
+.btn-primary:visited,.btn-primary:focus,.btn-primary:active,.btn-primary.active{
+ background-color: #5bb75b !important;
+ border-color: #289028 !important;
+ color: #fff;
+}
+.btn-primary:hover{
+ background-color: #289028 !important;
+ border-color: #289028 !important;
+ color: #fff;
+}
+._actions_popup {
+ padding: 4px;
+}
+
+._actions_popup .control_flow {
+ padding-left: 20%;
+}
+
+._actions_popup ul {
+ padding: 0;
+ margin: 0;
+}
+
+._actions_popup h4 {
+ font-size: 10px;
+ font-weight: bold;
+}
+
+._actions_popup li {
+ list-style: none;
+ float: left;
+ width: 53px;
+ height: 50px;
+ border: 1px solid silver;
+ margin: 3px;
+ padding: 10px;
+ text-align: center;
+ font-size: 11px;
+ border-radius: 6px;
+ cursor: pointer;
+}
+.labels {
+ width:70px;
+}
+.error{
+ border: 2px solid red;
+}
+.validated{
+ border: 2px solid #4cae4c;
+}
+.notification {
+ width:40%;
+ left:50%;
+ position: relative;
+}
+.redColor{
+ color: red;
+}
+.killNode{
+ width: 150px;
+}
+.zoom-buttons {
+ position: absolute;
+ top: 9px;
+ right: 2%;
+}
+.legend {
+ right: 1%;
+
+}
+.globlaPropetiesInfo{
+ width: 80%;
+ left: 10%;
+ position: relative;
+}
+.zoom-buttons .btn {
+ font-size: 8px;
+}
+
+.requiredField {
+ color: red;
+}
+#search-bar .twitter-typeahead {
+ position: inherit !important;
+}
+#search-bar .tt-hint {
+ display: none !important;
+}
+#search-bar .bootstrap-tagsinput {
+ width: 260px;
+ border-radius: 0px;
+ line-height: 20px;
+ vertical-align: top;
+ height: 30px;
+ overflow-y: auto;
+}
+#search-bar .bootstrap-tagsinput .label-info{
+ background-color : #5bb75b;
+}
+#search-bar .tt-menu {
+ position: absolute;
+ top: 100%;
+ left: inherit !important;
+ z-index: 100;
+ border: 1px solid rgba(0,0,0,0.2);
+ margin-top: 7px;
+ background-color: white;
+ width: 60% !important;
+ padding: 0 5px;
+}
+#job-detail-app-path{
+ overflow-x: scroll;
+}
+
+#search-bar .search-icon {
+ display: inline-block;
+ border: 1px solid #ccc;
+ padding: 4px 10px;
+ border-left: 0px;
+ border-bottom-right-radius: 3px;
+ border-top-right-radius: 3px;
+}
+
+#startDate, #endDate {
+ height: 30px;
+ width: 162px;
+}
+
+.jobTitle {
+ height: 24px;
+ margin-left: 10px;
+ line-height: 30px;
+}
+
+.info {
+ float: left;
+ margin-right: 20px;
+ color: #777;
+ padding-top: 4px;
+}
+
+.pages {
+ margin-right: 40px;
+}
+
+.listing-footer {
+ border-top: 1px solid #d2d2d2;
+ border-bottom: 1px solid #d2d2d2;
+ padding: 3px 0px;
+ margin: 0 0 5px 0;
+}
+
+.page-selector {
+ padding: 4px 12px;
+ border-radius: 0px;
+}
+
+.listing thead {
+ background: #f5f5f5;
+}
+
+.listing th {
+ border-left : 1px solid #ddd;
+ border-bottom: 1px solid #ddd !important;
+}
+
+.listing tr th:first-child {
+ border-left: 0;
+}
+
+.listing > tbody > tr > td {
+ vertical-align: middle;
+ padding: 5px;
+ border-left: 0;
+ border-right: 0;
+}
+
+.job-listing > tbody > tr > td {
+ border-bottom: 1px solid #ddd;
+}
+
+
+.pointer {
+ cursor: pointer;
+}
+
+#job-details .nav-tabs {
+ background: #eee;
+ margin-bottom: 10px;
+}
+
+.preview pre.prettyprint {
+ border: none;
+ background: #fff;
+}
+
+.preview pre {
+ border: none;
+ background: #fff;
+}
+
+.date-picker {
+ position: relative;
+}
+
+.actions-col .loading-container{
+ position: relative;
+}
+
+#bulk-action-loader .loading-container{
+ position: relative;
+}
+
+.decisionNodeHandler {
+ left: 10%;
+ bottom: 30%;
+ position: relative;
+}
+
+.decisionBox {
+ right: 10px;
+ top: 13%;
+ width:75px;
+ position: relative;
+}
+
+.pages .open .dropdown-menu {
+ max-height: 120px;
+ overflow-y: scroll;
+ min-width : 75px;
+ width : 75px;
+}
+
+#action-details .panel-heading {
+ padding: 10px 15px;
+}
+
+#bulk-action-button .dropdown-menu li .fa {
+ margin-right: 10px;
+}
+
+#dashboard, #dashboard .panel, .panel-heading, .panel-body {
+ border-radius: 0px;
+}
+
+#job-details, #job-details .panel, .panel-heading, .panel-body {
+ border-radius: 0px;
+}
+
+#dashboard .listing-error{
+ text-align: center;
+}
+
+#dashboard .panel {
+ border: 0;
+ border-bottom: 1px solid #ddd;
+}
+
+#loading {
+ position: absolute;
+ top: 90px;
+ left: 0;
+ display: none;
+ width: 100vw;
+ height: 80vh;
+ overflow: hidden;
+ z-index: 1;
+}
+
+.loader img {
+ height: 40px;
+ width: 50px;
+}
+
+#search-table {
+ margin-bottom: 0px;
+}
+
+#search-table .bulk-actions-buttons {
+ margin-left: -124px;
+ margin-top: 3px;
+ position: fixed;
+ transition: all .5s ease;
+}
+
+#search-table .bulk-actions-buttons:before {
+ display: block;
+ content: "";
+ width: 0;
+ height: 0;
+ left: -10px;
+ top: 10px;
+ border: 5px solid transparent;
+ border-right: 10px solid #666;
+ position: absolute;
+}
+
+#search-table .bulk-actions-buttons.shown {
+ margin-left: 30px;
+}
+
+#search-table .actions-col {
+ white-space: nowrap;
+ text-align: center;
+ min-width: 150px;
+ }
+
+ .btn-kill:hover,
+ .btn-kill:focus {
+ background: #d9534f;
+ color: #fff;
+ border-color: red;
+ }
+
+ .actions-col .btn {
+ color: gray;
+ cursor: not-allowed;
+ opacity: .5;
+ font-size: 10px;
+ }
+
+ .actions-col .btn.isOn {
+ color: #000;
+ cursor: pointer;
+ opacity: 1;
+ }
+ .pages .btn {
+ /*color: #5bb75b;*/
+ }
+
+ .pages .btn.disabled {
+ color: gray;
+ }
+
+ .pages .btn-primary {
+ color: #fff;
+ }
+ .cbox {
+ width: 20px;
+ }
+
+ .main-panel {
+ padding: 0px;
+ }
+
+ #dashboard .main-panel {
+ padding: 5px;
+ }
+
+ .first-col {
+ text-align: center;
+ }
+
+
+
+.DONEWITHERROR,
+.FAILED,
+.KILLED {
+ color: #d9534f;
+}
+
+.RUNNING {
+ color: green;
+}
+
+.PAUSED,
+.PAUSEDWITHERROR,
+.PREPPAUSED,
+.RUNNINGWITHERROR,
+.SUSPENDED,
+.SUSPENDEDWITHERROR {
+ color: #f0ad4e;
+}
+
+.SUCCEEDED {
+ color: #5cb85c;
+}
+
+.mb0 {
+ margin-bottom: 0;
+}
+
+.pb5 {
+ padding-bottom: 5px;
+}
+
+.navbar-header {
+ width: 100%;
+}
+
+#create-new-button {
+ float: right;
+ padding-top: 8px;
+}
+
+.width100Per{
+ width: 100%;
+}
+.hdfs-browse{
+ max-height: 500px;
+ overflow: scroll;
+}
+#wf_title{
+ margin-top: 10px;
+ float: left;
+ width: 115px;
+ font-size: 12px;
+}
+.backto-dashboard{
+ text-decoration: blink !important;
+}
+.file-upload-control input[type="file"]{
+ width: 200px;
+ display: inline;
+ border: 1px solid #ccc;
+}
+.file-upload-control input[type="button" i], input[type="submit" i], input[type="reset" i], input[type="file" i]::-webkit-file-upload-button, button {
+ padding: 6px 12px;
+ border-radius: 4px;
+ background-color: #fff;
+ line-height: 1.42857143;
+ border: none;
+}
+.close-icon{
+ font-size: 18px;
+ font-weight: bold;
+ line-height: 1;
+ text-shadow: 0 1px 0 #fff;
+ color: #AFAFAF;
+ background: transparent;
+ padding: 3px;
+}
+#flow-designer{
+ position: relative;
+ top:10%;
+}
+.designer-main-panel{
+ position: relative;
+ width: 100%;
+ border: none;
+ height: 0px;
+}
+.connector_overlay_new {
+ white-space: nowrap;
+ border: 1px solid #ddd;
+ background: #E5E5E5;
+ border-radius: 30px;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ text-align: center;
+ cursor: pointer;
+ transform: scale(0.8);
+ font-size: 12px;
+}
+.connector_overlay_new:hover .node_actions{
+ opacity: 1;
+}
+.placeholder-node{
+ width:1px;
+ height: 1px;
+ background: transparent;
+ padding-left: 0px;
+ padding-right: 0px;
+ margin-left: 4px;
+ /* transform: translate(0%, -6%);*/
+}
+.decision_end{
+ width: 10px;
+ height: 10px;
+ border-radius: 16px;
+}
+#decision-properties-table {
+ width: 70%;
+ margin-left: 50px;
+}
+.preview-xml{
+ border: none;
+ padding: 8px;
+}
+.workflow-actions-pop{
+ width: 300px;
+}
+#killnodes-container{
+ width: 300px;
+ height: 100px;
+ padding: 5px;
+ border-radius: 3px;
+ /*border: 2px solid #ccc;*/
+}
+.decision-add-branch-popover{
+ position: absolute;
+ left: 44px;
+ top: -7px;
+ z-index: 5;
+}
+
+.jsplumb-connector { z-index:1; }
+.jsplumb-endpoint { z-index:2; }
+.jsplumb-overlay { z-index:3; }
+#selector-content .panel-body{
+ width:400px;
+}
+
+
+
+.node-test {
+ background-color: white;
+ border: 3px solid #346789;
+ color: black;
+ font-family: helvetica;
+ font-size: 0.8em;
+ height: 12em;
+ opacity: 0.8;
+ padding: 0.5em;
+ position: absolute;
+ width: 14em;
+ z-index: 20;
+ text-align:center;
+}
+
+.dragActive {
+ border: 3px solid #346789;
+}
+.decisionSplit {
+ border-radius: 24px;
+ width: 24px;
+ height: 24px;
+ left: 70% !important;
+ top: 67% !important;
+ transform:rotate(315deg) !important;
+}
+.forkSplit {
+ border-radius: 24px;
+ width: 24px;
+ height: 18px;
+ left: 50% !important;
+ top: 75% !important;
+}
+.forkAction{
+}
+.control_flow_node {
+ cursor: not-allowed;
+}
+.join {
+background: #fff;
+}
+.default-condition{
+ background-color: blue;
+}
+.query-text-area {
+ width: 540px;
+}
+.decisionText{
+ position: absolute;
+ transform: rotate(315deg);
+ top: 30%;
+ left: 5px;
+ right: 0px;
+}
+.decisionIcon{
+ position: relative;
+ right: 5px;
+}
+.actionNodes li{
+ padding: 10px 0px !important;
+ height: auto;
+}
+.dr_action_fork{
+ width: 45px !important;
+ height: 45px !important;
+ padding: 13px 0px !important;
+}
+.dr_action_switch{
+ width: 50px !important;
+ height: 45px !important;
+ padding: 10px 0px !important;
+}
+.paddingClass{
+ padding-top: 0.5%;
+}
+.margin0{
+ margin-bottom: 0px;
+}
+.editor-icon-title span{
+ padding-left: 5px;
+}
+.icon_map_red{
+ background: url(data:image/png;base64,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)
+}
+.node_actions i{
+ border-right: 1px solid #ababab;
+ border-radius: 0px;
+ min-width: 26px;
+}
+.node_actions i:last-child {
+ border-right:none;
+}
+.elipsisClass{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -o-text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 30%;
+}
+.wf-path {
+ font-size: 12px;
+ float: left;
+ color: gray;
+ margin-top: 17px;
+ max-width: 150px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+.cred-type{
+ font-size: 12px;
+ color: gray;
+ margin-left: 5px;
+}
+.labelCheckbox{
+ padding-left:35px !important;
+}
+.file-op-setting{
+ padding-left: 105px;
+}
+.rightAlign{
+ text-align: left !important;
+}
+.bottom-margin-zero{
+ margin-bottom: 5px !important;
+}
+#credentials_config .fa-trash-o{
+ color: red;
+ cursor: pointer;
+}
+.check-class{
+ padding-left: 3%;
+}
+.popover-title{
+ background-color:#fff
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/application.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/application.hbs
new file mode 100644
index 0000000..29fb89e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/application.hbs
@@ -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.
+}}
+ {{#header-logo-user-bar}}{{/header-logo-user-bar}}
+<div id="cntnr" class="cntnr">
+ {{outlet}}
+</div>