ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yus...@apache.org
Subject [09/12] ambari git commit: AMBARI-17213. Create ambari workflow designer contrib view. (Venkat Ranganathan via yusaku)
Date Tue, 09 Aug 2016 20:15:02 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-parameters.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-parameters.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-parameters.js
new file mode 100644
index 0000000..1f75e64
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-parameters.js
@@ -0,0 +1,80 @@
+/*
+*    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 EmberValidations,{ validator } from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations, {
+  initialize : function(){
+    if(this.get('parameters') === undefined || this.get('parameters') === null){
+      this.set('parameters',{});
+    }
+    if(this.get('parameters.configuration') === undefined){
+      this.set("parameters.configuration",{});
+      this.set("parameters.configuration.property", Ember.A([]));
+    }
+    this.sendAction('register','workflowParameters',this);
+
+  }.on('init'),
+  validations : {
+    'parameters': {
+      inline : validator(function() {
+        var nameMap = [], errorMsg = undefined;
+        if(this.get('parameters.configuration.property')){
+          this.get('parameters.configuration.property').forEach(function(item, index){
+            if(!item.name){
+              errorMsg = "Name cannot be blank";
+            } else if(nameMap.indexOf(item.name) > -1){
+              errorMsg = "Name cannot be duplicate";
+            } else{
+              nameMap.push(item.name);
+            }
+          });
+          if(errorMsg){
+            return errorMsg;
+          }
+        }
+      })
+    }
+  },
+  rendered : function(){
+    this.$('#workflow_parameters_dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#workflow_parameters_dialog').modal('show');
+    this.$('#workflow_parameters_dialog').modal().on('hidden.bs.modal', function() {
+      if(this.get('saveClicked')){
+        this.sendAction('saveWorkFlowParam');
+      }else{
+        this.sendAction('closeWorkFlowParam');
+      }
+    }.bind(this));
+  }.on('didInsertElement'),
+  actions : {
+    register(component, context){
+      this.set('nameValueContext', context);
+    },
+    saveParameters (){
+      this.get("nameValueContext").trigger("bindInputPlaceholder");
+      this.validate().then(function(){
+        this.set('saveClicked', true);
+        this.$('#workflow_parameters_dialog').modal('hide');
+      }.bind(this)).catch(function(e){
+      }.bind(this));
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-sla.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-sla.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-sla.js
new file mode 100644
index 0000000..dac325f
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-sla.js
@@ -0,0 +1,50 @@
+/*
+*    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 {SlaInfo} from '../domain/sla-info'
+import EmberValidations from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations,{
+  slaInfo : {},
+  initialize : function(){
+    this.set('slaInfo',Ember.copy(this.get('workflowSla')));
+  }.on('init'),
+  rendered : function(){
+    this.$('#workflow_sla_dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#workflow_sla_dialog').modal('show');
+    this.$('#workflow_sla_dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('showWorkflowSla', false);
+    }.bind(this));
+  }.on('didInsertElement'),
+  actions : {
+    saveWorkflowSla () {
+      this.get('slaContext').validate().then(()=>{
+        this.set('workflowSla', this.get('slaInfo'));
+        this.$('#workflow_sla_dialog').modal('hide');
+      }.bind(this)). catch(()=>{
+
+      });
+
+    },
+    register (name, context) {
+      this.set('slaContext', context);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/controllers/application.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/controllers/application.js b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/application.js
new file mode 100644
index 0000000..689ec0d
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/application.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.Controller.extend({
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/controllers/dashboard.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/controllers/dashboard.js b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/dashboard.js
new file mode 100644
index 0000000..9760ddb
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/dashboard.js
@@ -0,0 +1,81 @@
+/*
+*    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.Controller.extend({
+  actions: {
+    launchDesign: function () {
+      this.transitionToRoute('design');
+    },
+    doRefresh : function(){
+      this.get('target.router').refresh();
+    },
+    onJobAction: function (params, deferred) {
+      if (Ember.ENV.API_FAILED) {
+        return { error: "Remote API Failed." };
+      }
+      var url = [Ember.ENV.API_URL,
+        "/v2/job/", params.id, "?action=", params.action,'&user.name=oozie'
+      ].join("");
+      var jobActionParams = {
+        url: url,
+        method: 'PUT',
+        beforeSend: function (xhr) {
+          xhr.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+          xhr.setRequestHeader("X-Requested-By", "Ambari");
+          if(params.action.indexOf('rerun') > -1){
+            xhr.setRequestHeader("Content-Type","application/xml");
+          }
+        }
+      };
+      if(params.action.indexOf('rerun') > -1){
+        jobActionParams.data = params.conf;
+      }
+      Ember.$.ajax(jobActionParams).done(function(){
+        deferred.resolve();
+      }).fail(function(){
+        deferred.reject();
+      });
+    },
+    onBulkAction : function(params, deferred){
+      if (Ember.ENV.API_FAILED) {
+        return { error: "Remote API Failed." };
+      }
+      var url = [Ember.ENV.API_URL,
+        "/v2/jobs?jobtype=", params.jobType,
+        "&offset=", params.start,
+        "&len=", params.len,
+        "&filter=", params.filter,
+        "&action=", params.action,
+        "&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(response){
+        deferred.resolve(response);
+      }).fail(function(response){
+        deferred.reject(response);
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/controllers/design.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/controllers/design.js b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/design.js
new file mode 100644
index 0000000..285ad19
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/design.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.Controller.extend({
+  queryParams: "appPath",
+  appPath : null,
+  model: function(params) {
+    return {};
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/controllers/job.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/controllers/job.js b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/job.js
new file mode 100644
index 0000000..2c90be4
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/controllers/job.js
@@ -0,0 +1,60 @@
+/*
+*    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.Controller.extend({
+  from : null,
+  fromType : null,
+  actions : {
+    close : function(){
+      this.sendAction('onCloseJobDetails');
+    },
+    doRefresh : function(){
+      this.get('target.router').refresh();
+    },
+    showWorkflow : function(workflowId){
+      this.transitionToRoute('job', {
+        queryParams: {
+          jobType: 'wf',
+          id: workflowId,
+          from : this.get('model.coordJobId'),
+          fromType : this.get('model.jobType')
+        }
+      });
+    },
+    showCoord : function(coordJobId){
+      this.transitionToRoute('job', {
+        queryParams: {
+          jobType: 'coords',
+          id: coordJobId,
+          from : this.get('model.bundleJobId'),
+          fromType : this.get('model.jobType')
+        }
+      });
+    },
+    back : function (){
+      this.transitionToRoute('job', {
+        queryParams: {
+          jobType: this.get('fromType'),
+          id: this.get('from'),
+          from : null,
+          fromType : null
+        }
+      });
+    },
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js
new file mode 100644
index 0000000..33204ea
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/actionjob_hanlder.js
@@ -0,0 +1,411 @@
+/*
+*    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 {MappingMixin,ConfigurationMapper,PrepareMapper} from "../domain/mapping-utils";
+var ActionJobHandler=Ember.Object.extend(MappingMixin,{
+  type:"actionJob",
+  context : {},
+  configurationMapper:ConfigurationMapper.create({}),
+  prepareMapper:PrepareMapper.create({}),
+  setContext(context){
+    this.context = context;
+  },
+  getContext(){
+    return this.context;
+  },
+  handle(nodeDomain,nodeObj,nodeName){
+    var actionObj={};
+    nodeObj[this.get("actionType")]=actionObj;
+    if (this.get("nameSpace")){
+      var schemaVersion=this.schemaVersions.getActionVersion(this.get("actionType"));
+      if (this.get("nameSpace")){
+        var schema=this.get("nameSpace");
+        if (schemaVersion){
+          schema=CommonUtils.extractSchema(schema)+":"+schemaVersion;
+        }
+        nodeObj[this.get("actionType")]["_xmlns"]=schema;
+      }
+    }
+    this.handleMapping(nodeDomain,actionObj,this.mapping,nodeName);
+  },
+  validate(nodeDomain){
+    //overwrite in implmentations and return array of errors object.
+  },
+  handleImport(actionNode,json){
+    this.handleImportMapping(actionNode,json,this.mapping);
+  }
+});
+var JavaActionJobHandler=ActionJobHandler.extend({
+  actionType:"java",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"main-class",domain:"mainClass",mandatory:true},
+      {xml:"java-opts",domain:"javaOpts"},
+      {xml:"java-opt",domain:"javaOpt",occurs:"many", domainProperty:"value"},
+      {xml:"arg",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"},
+      {xml:"capture-output",domain:"captureOutput",ignoreValue:true}
+    ];
+  },
+
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+  }
+});
+var PigActionJobHandler=ActionJobHandler.extend({
+  actionType:"pig",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"script",domain:"script",mandatory:true},
+      {xml:"param",domain:"param",domainProperty:"value",occurs:"many"},
+      {xml:"argument",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"}
+    ];
+  }
+});
+var HiveActionJobHandler=ActionJobHandler.extend({
+  actionType:"hive",
+  nameSpace:"uri:oozie:hive-action:0.6",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"script",domain:"script"},
+      {xml:"query",domain:"query"},
+      {xml:"param",domain:"params",domainProperty:"value",occurs:"many"},
+      {xml:"argument",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"}
+    ];
+  },
+  validate(nodeDomain){
+    if (Ember.isBlank(nodeDomain.script) && Ember.isBlank(nodeDomain.query)){
+      return [{message : "Either script or query to be set."}];
+    }
+  }
+});
+var Hive2ActionJobHandler=ActionJobHandler.extend({
+  actionType:"hive2",
+  nameSpace:"uri:oozie:hive2-action:0.2",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"jdbc-url",domain:"jdbc-url",mandatory:true},
+      {xml:"password",domain:"password"},
+      {xml:"script",domain:"script"},
+      {xml:"query",domain:"query"},
+      {xml:"param",domain:"params",domainProperty:"value",occurs:"many"},
+      {xml:"argument",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"}
+    ];
+  },
+  validate(nodeDomain){
+    if (Ember.isBlank(nodeDomain.script) && Ember.isBlank(nodeDomain.query)){
+      return [{message : "Either script or query to be set."}];
+    }
+  }
+});
+
+var SqoopActionJobHandler=ActionJobHandler.extend({
+  actionType:"sqoop",
+  nameSpace:"uri:oozie:sqoop-action:0.4",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"command",domain:"command"},
+      {xml:"argument",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"}
+    ];
+  },
+  validate(nodeDomain){
+    if (Ember.isBlank(nodeDomain.command) && nodeDomain.args.length<1){
+      return [{message : "Either command or arguments have to be set."}];
+    }
+  }
+});
+var ShellActionJobHandler=ActionJobHandler.extend({
+  actionType:"shell",
+  nameSpace:"uri:oozie:shell-action:0.3",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"exec",domain:"exec",mandatory:true},
+      {xml:"argument",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"env-var",domain:"envVar",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"},
+      {xml:"capture-output",domain:"captureOutput",ignoreValue:true}
+    ];
+  },
+
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+  }
+});
+var SparkActionJobHandler=ActionJobHandler.extend({
+  actionType:"spark",
+  nameSpace:"uri:oozie:spark-action:0.2",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"master",domain:"master",mandatory:true,displayName:"Runs On"},
+      {xml:"mode",domain:"mode"},
+      {xml:"name",domain:"sparkName",mandatory:true},
+      {xml:"class",domain:"class"},
+      {xml:"jar",domain:"jar",mandatory:true,displayName:"Application"},
+      {xml:"spark-opts",domain:"sparkOpts"},
+      {xml:"arg",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"}
+    ];
+  },
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+  }
+});
+var SubWFActionJobHandler=ActionJobHandler.extend({
+  actionType:"sub-workflow",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"app-path",domain:"appPath",mandatory:true},
+      {xml:"propagate-configuration",domain:"propagate-configuration", ignoreValue:true},
+      {xml:"configuration",customHandler:this.configurationMapper}
+    ];
+  }
+});
+var DistCpJobHandler=ActionJobHandler.extend({
+  actionType:"distcp",
+  nameSpace:"uri:oozie:distcp-action:0.2",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.handlePrepare},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"java-opts",domain:"javaOpts"},
+      {xml:"arg",domain:"args",occurs:"many",domainProperty:"value"},
+    ];
+  },
+
+});
+
+var SshActionJobHandler=ActionJobHandler.extend({
+  actionType:"ssh",
+  nameSpace:"uri:oozie:ssh-action:0.2",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"host",domain:"host"},
+      {xml:"command",domain:"command"},
+      {xml:"args",domain:"args",occurs:"many",domainProperty:"value"},
+      {xml:"arg",domain:"arg",occurs:"many",domainProperty:"value"},
+      {xml:"capture-output",domain:"captureOutput",ignoreValue:true}
+    ];
+  },
+
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+  }
+});
+
+var EmailActionJobHandler=ActionJobHandler.extend({
+  actionType:"email",
+  nameSpace:"uri:oozie:email-action:0.2",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"to",domain:"to",mandatory:true},
+      {xml:"cc",domain:"cc"},
+      {xml:"bcc",domain:"bcc"},
+      {xml:"subject",domain:"subject",mandatory:true},
+      {xml:"body",domain:"body",mandatory:true},
+      {xml:"content_type",domain:"content_type"},
+      {xml:"attachment",domain:"attachment"}
+
+    ];
+  },
+
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+  }
+});
+
+
+var MapRedActionJobHandler=ActionJobHandler.extend({
+  actionType:"map-reduce",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"job-tracker",domain:"jobTracker"},
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"prepare",customHandler:this.prepareMapper},
+      {xml:"job-xml",domain:"jobXml",occurs:"many",domainProperty:"value"},
+      {xml:"config-class", domain:"config-class"},
+      {xml:"configuration",customHandler:this.configurationMapper},
+      {xml:"file",domain:"files",occurs:"many",domainProperty:"value"},
+      {xml:"archive",domain:"archives",occurs:"many",domainProperty:"value"}
+    ];
+  },
+
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+  }
+});
+
+var FSActionJobHandler=ActionJobHandler.extend({
+  actionType:"fs",
+  mapping:null,
+  init(){
+    this.mapping=[
+      {xml:"name-node",domain:"nameNode"},
+      {xml:"configuration",customHandler:this.configurationMapper}
+    ];
+  },
+  handle(nodeDomain,nodeObj,nodeName){
+    this._super(nodeDomain,nodeObj,nodeName);
+    if (!nodeDomain.fsOps){
+      return;
+    }
+    nodeDomain.fsOps.forEach(function(fsop){
+      if (!nodeObj.fs[fsop.type]){
+        nodeObj.fs[fsop.type]=[];
+      }
+      switch (fsop.type) {
+        case "delete":
+        nodeObj.fs["delete"].push({"_path":fsop.settings.path});
+        break;
+        case "mkdir":
+        nodeObj.fs["mkdir"].push({"_path":fsop.settings.path});
+        break;
+        case "move":
+        nodeObj.fs["move"].push({"_source":fsop.settings.source,"_target":fsop.settings.target});
+        break;
+        case "touchz":
+        nodeObj.fs["touchz"].push({"_path":fsop.settings.path});
+        break;
+        case "chmod":
+        var conf={"_path":fsop.settings.path,"_permissions":fsop.settings.permissions,"_dir-files":fsop.settings.dirfiles};
+        if (fsop.settings.recursive){
+          conf["recursive"]="";
+        }
+        nodeObj.fs["chmod"].push(conf);
+        break;
+        case "chgrp":
+        var conf={"_path":fsop.settings.path,"_group":fsop.settings.group,"_dir-files":fsop.settings.dirfiles};
+        if (fsop.settings.recursive){
+          conf["recursive"]="";
+        }
+        nodeObj.fs["chgrp"].push(conf);
+        break;
+        default:
+      }
+    });
+  },
+  handleImport(actionNode,json){
+    this._super(actionNode,json);
+    var commandKeys=["delete","mkdir","move","chmod","touchz","chgrp"];
+    var fsOps=actionNode.domain.fsOps=[];
+    Object.keys(json).forEach(function(key){
+      if (commandKeys.contains(key)){
+        var fileOpsJson=null;
+        if (!Ember.isArray(json[key])){
+          fileOpsJson=[json[key]];
+        }else{
+          fileOpsJson=json[key];
+        }
+        fileOpsJson.forEach(function (fileOpJson) {
+          var fsConf={};
+          fsOps.push(fsConf);
+          fsConf.type=key;
+          var settings=fsConf.settings={};
+          switch (key) {
+            case "delete":
+            settings.path=fileOpJson._path;
+            break;
+            case "mkdir":
+            settings.path=fileOpJson._path;
+            break;
+            case "touchz":
+            settings.path=fileOpJson._path;
+            break;
+            case "move":
+            settings.source=fileOpJson._source;
+            settings.target=fileOpJson._target;
+            break;
+            case "chmod":
+            settings.path=fileOpJson._path;
+            settings.permissions=fileOpJson._permissions;
+            settings.dirfiles=fileOpJson["_dir-files"];
+            settings.recursive=fileOpJson["recursive"]?true:false;
+            break;
+            case "chgrp":
+            settings.path=fileOpJson._path;
+            settings.group=fileOpJson._group;
+            settings.dirfiles=fileOpJson["_dir-files"];
+            settings.recursive=fileOpJson["recursive"]?true:false;
+            break;
+          }
+        });
+      }
+    });
+  }
+});
+export{ActionJobHandler,JavaActionJobHandler,PigActionJobHandler,HiveActionJobHandler,SqoopActionJobHandler,ShellActionJobHandler, EmailActionJobHandler,SparkActionJobHandler,MapRedActionJobHandler, Hive2ActionJobHandler, SubWFActionJobHandler, DistCpJobHandler, SshActionJobHandler, FSActionJobHandler};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
new file mode 100644
index 0000000..e208f83
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
@@ -0,0 +1,50 @@
+/*
+*    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';
+var DefaultLayoutManager= Ember.Object.extend({
+  doDagreLayout(nodes,edges){
+    var g = new dagre.graphlib.Graph();
+    g.setGraph({rankdir:"TB", nodesep:100,edgesep:200,marginx:40,ranksep:130});
+    g.setDefaultEdgeLabel(function() { return {}; });
+
+    for (var i = 0; i < nodes.length; i++) {
+      var n = Ember.$(nodes[i]);
+      g.setNode(n.attr("id"), {width: n.width(), height: n.height()});
+    }
+
+    for (var i = 0; i < edges.length; i++) {
+      var c = edges[i];
+      g.setEdge(c.source,c.target);
+    }
+    dagre.layout(g);
+    return g;
+  },
+  doLayout(component,nodes,edges){
+    var g=this.doDagreLayout(nodes,edges);
+    g.nodes().forEach(function(v) {
+      try{
+        var nodeWidth=component.$("#" + v).width();
+        var displacement=150-Math.floor(nodeWidth/2);
+        Ember.$("#" + v).css("left", g.node(v).x+displacement + "px");
+        Ember.$("#" + v).css("top",g.node(v).y+ "px");
+      }catch(err){
+      }
+    });
+  }
+});
+export {DefaultLayoutManager};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
new file mode 100644
index 0000000..0566e06
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
@@ -0,0 +1,103 @@
+/*
+*    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';
+var FindNodeMixin= Ember.Mixin.create({
+  findNodeById(startNode,id){
+
+    return this.findNodeByIdInternal(startNode,id);
+  },
+  findNodeByIdInternal(node,id){
+    var self=this;
+    if (node.get("id")===id){
+      return node;
+    }else{
+      if (node.transitions){
+        for (var i = 0; i < node.transitions.length; i++) {
+          var transition=node.transitions[i];
+          var result=self.findNodeByIdInternal(transition.getTargetNode(true),id);
+          if (result){
+            return result;
+          }
+        }
+      }else{
+        return null;
+      }
+    }
+  },
+  getOKToNode(node){
+    var okToNode;
+    if (!node){
+      okToNode = null;
+    }else{
+      var transitions = node.transitions;
+      transitions.forEach(function(trans){
+        if (!trans.condition){
+          okToNode = trans.targetNode;
+          return;
+        }
+      });
+    }
+    return okToNode;
+  },
+  getDesendantNodes(node, ignoreEndNode){
+    if (!node){
+      return null;
+    }
+    var currNode=null;
+    var nodes = [], nxtPath = node.getTargets();
+    for(var i =0; i< nxtPath.length; i++){
+      currNode = nxtPath[i];
+      do {
+        if(this.insertUniqueNodes(currNode, nodes) && currNode){
+          nodes.push(currNode);
+        }
+        var nodesList = currNode.getTargets();
+        if(nodesList.length > 1){
+          for(var j=0; j<nodesList.length; j++) {
+            if(nodesList[j].getTargets().length>1){
+              var tmp = this.getDesendantNodes(nodesList[j]);
+              if(tmp.length){
+                nodes = nodes.concat(tmp);
+              }
+            } else if(this.insertUniqueNodes(nodesList[j], nodes) && nodesList[j]){
+              nodes.push(nodesList[j]);
+              currNode = nodesList[j];
+            } else {
+              currNode = nodesList[j];
+            }
+          }
+        } else {
+          currNode = nodesList[0];
+        }
+      } while(currNode && currNode.get("id") && currNode.get("id") !== "node-end");
+    }
+    if(!ignoreEndNode && currNode){
+      nodes.push(currNode);
+    }
+    return nodes;
+  },
+  insertUniqueNodes(currNode, nodes){
+    if(nodes.indexOf(currNode) > -1){
+    } else {
+      if (!( currNode.isKillNode() || currNode.isPlaceholder() || currNode.isJoinNode() || currNode.isDecisionEnd())){
+        return true;
+      }
+    }
+  },
+});
+export{FindNodeMixin};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js
new file mode 100644
index 0000000..0ee985e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.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';
+var IdGen = Ember.Object.extend({
+  idCount: 0,
+  nameCount: 0,
+  usedIds: [],
+  generateNodeId(){
+    return "node_"+(this.idCount++);
+  },
+  generateNodeName(){
+    return this.nameCount++;
+  },
+  reset(){
+    this.nameCount=0;
+    this.idCount=0;
+  }
+});
+var idGen=IdGen.create({});
+export {idGen};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager1.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager1.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager1.js
new file mode 100644
index 0000000..0cd306a
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager1.js
@@ -0,0 +1,88 @@
+/*
+*    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';
+var LayoutManager1= Ember.Object.extend({
+  doLayout(component,nodes,edges,workflow){
+    var levelMatrix = [];
+    var adjancencyMatrix = {};
+    for (var i = 0; i < edges.length; i++) {
+      var c = edges[i];
+      if(!adjancencyMatrix[c.source.id]){
+        adjancencyMatrix[c.source.id] = [];
+      }
+      adjancencyMatrix[c.source.id].push(c.target.id);
+    }
+    var bfsArray = this.doBFS(nodes[0].id, adjancencyMatrix);
+    var level = 0;
+    bfsArray.forEach((item, index)=>{
+      if(!adjancencyMatrix[item]){
+        return;
+      }
+      adjancencyMatrix[item].forEach((value)=>{
+        if(!levelMatrix[level]){
+          levelMatrix[level] = [];
+        }
+        levelMatrix[level].push(value);
+      });
+      level++;
+    });
+    var startNodeOffset = component.$("#node-start").offset();
+    var top = Math.floor(startNodeOffset.top);
+    var left = Math.floor(startNodeOffset.left);
+    levelMatrix.forEach((nodeArray, level)=>{
+      var levelLength = nodeArray.length;
+      var levelSplit = left/levelLength;
+      nodeArray.forEach((node, idx, array)=>{
+        if(levelLength == 1){
+          Ember.$("#" + node).css("top", top+(level*100)+ "px");
+        }else{
+          Ember.$("#" + node).css("top", top+ "px");
+          if(idx < levelLength/2){
+            Ember.$("#" + node).css("left", left-(idx*100) + "px");
+          }else if(idx === levelLength/2){
+            Ember.$("#" + node).css("left", left + "px");
+          }else{
+            Ember.$("#" + node).css("left", left+(idx*100) + "px");
+          }
+        }
+      });
+    });
+  },
+  doBFS (root, adjancencyMatrix){
+    var bfsResult = [];
+    var level = 0;
+    var visited = {};
+    visited[root] = true;
+    var queue = [];
+    queue.push(root);
+    while(queue.length !== 0){
+      root = queue.shift();
+      bfsResult.push(root);
+      if(!adjancencyMatrix[root]){
+        continue;
+      }
+      adjancencyMatrix[root].forEach(function(node){
+        if(!visited[node]){
+          visited[node] = true;
+          queue.push(node);
+        }
+      });
+    }
+    return bfsResult;
+  },
+});
+export {LayoutManager1};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager2.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager2.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager2.js
new file mode 100644
index 0000000..d82b89e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/layout-manager2.js
@@ -0,0 +1,87 @@
+/*
+*    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';
+var LayoutManager1= Ember.Object.extend({
+  doLayout(component,nodes,edges,workflow){
+    var levelMatrix = [];
+    var adjancencyMatrix = {};
+    for (var i = 0; i < edges.length; i++) {
+      var c = edges[i];
+      if(!adjancencyMatrix[c.source.id]){
+        adjancencyMatrix[c.source.id] = [];
+      }
+      adjancencyMatrix[c.source.id].push(c.target.id);
+    }
+    var bfsArray = this.doBFS(nodes[0].id, adjancencyMatrix);
+    var level = 0;
+    levelMatrix[level] = [];
+    levelMatrix[level++].push(nodes[0].id);
+    bfsArray.forEach((item, index)=>{
+      if(!adjancencyMatrix[item]){
+        return;
+      }
+      adjancencyMatrix[item].forEach((value)=>{
+        if(!levelMatrix[level]){
+          levelMatrix[level] = [];
+        }
+        levelMatrix[level].push(value);
+      });
+      level++;
+    });
+    var top = 0;
+    var left = 400;
+    var startNodeWidth = component.$("#"+nodes[0].id).width();
+    var center = left+(150-Math.floor(startNodeWidth/2));
+    levelMatrix.forEach((nodeArray, level)=>{
+      var levelLength = nodeArray.length;
+      nodeArray.forEach((node, idx, array)=>{
+        Ember.$("#" + node).css("top", top+(level*100)+ "px");
+        var nodeWidth=Math.round(component.$("#" + node).width()/10) * 10;
+        var avgPositionChange = 0;
+        var totalPositions = ((levelLength-1)*(levelLength)/2)*100;
+        var displacement = 150-Math.floor(nodeWidth/2);
+        var avgPositionChange = (totalPositions/levelLength);
+        var eltPosition = idx*100 - avgPositionChange;
+        var total = left + eltPosition + displacement;
+        Ember.$("#" + node).css("left", total + "px");
+      });
+    });
+  },
+  doBFS (root, adjancencyMatrix){
+    var bfsResult = [];
+    var level = 0;
+    var visited = {};
+    visited[root] = true;
+    var queue = [];
+    queue.push(root);
+    while(queue.length !== 0){
+      root = queue.shift();
+      bfsResult.push(root);
+      if(!adjancencyMatrix[root]){
+        continue;
+      }
+      adjancencyMatrix[root].forEach(function(node){
+        if(!visited[node]){
+          visited[node] = true;
+          queue.push(node);
+        }
+      });
+    }
+    return bfsResult;
+  },
+});
+export {LayoutManager1};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
new file mode 100644
index 0000000..7cb82e1
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
@@ -0,0 +1,254 @@
+/*
+*    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 {SlaInfo} from '../domain/sla-info';
+var MappingMixin= Ember.Mixin.create({
+  handleMapping(nodeDomain,nodeObj,mappings,nodeName){
+    var self=this;
+    mappings.forEach(function(mapping){
+      var nodeVals=[];
+      if (mapping.mandatory){
+        if (!(nodeDomain[mapping.domain] || mapping.customHandler)){
+          var msgForVal=mapping.domain;
+          if (mapping.displayName){
+            msgForVal=mapping.displayName;
+          }
+          self.getContext().addError({node:{name:nodeName}, message:"Mandatory element missing for "+msgForVal});
+        }
+      }
+      if (nodeDomain && mapping.domain && nodeDomain[mapping.domain]){
+        if (!mapping.occurs){
+          mapping.occurs="once";
+        }
+        var objs=[];
+        if (mapping.occurs==="once"){
+          objs.push(mapping.ignoreValue?"":nodeDomain[mapping.domain]);
+        }else{
+          if (mapping.domainProperty){
+            var tempObjs=[];
+            nodeDomain[mapping.domain].forEach(function(value){
+              tempObjs.push(value[mapping.domainProperty]);
+            });
+            objs=tempObjs;
+          }else{
+            objs=mapping.ignoreValue?"":nodeDomain[mapping.domain];
+          }
+        }
+        if (!Ember.isArray(objs) || Ember.isArray(objs)&& objs.length>0){
+          nodeObj[mapping.xml]=objs;
+        }
+      }else if (mapping.customHandler){
+        var result=mapping.customHandler.hanldeGeneration(nodeDomain,nodeObj);
+        if (result){
+          nodeObj[mapping.xml]=result;
+        }
+      }
+    });
+  },
+
+  handleImportMapping(actionNode,json,mappings){
+    var domain={};
+    if (json._xmlns){
+      var version=CommonUtils.extractSchemaVersion(json._xmlns);
+      this.schemaVersions.setActionVersion(actionNode.actionType,version);
+    }
+    actionNode.set("domain",domain);
+    mappings.forEach(function(mapping){
+      if (!mapping.occurs) {
+        mapping.occurs = "once";
+      }
+      if (mapping.domain && (json[mapping.xml] ||  json[mapping.xml]==="")){
+        if (mapping.occurs==="once"){
+          if (mapping.ignoreValue){
+            domain[mapping.domain]=json[mapping.xml]!==null || json[mapping.xml]!==undefined;
+          }else{
+            domain[mapping.domain]=json[mapping.xml];
+          }
+        }else{
+          if (!domain[mapping.domain]){
+            domain[mapping.domain]=Ember.A([]);
+          }
+          if (Ember.isArray(json[mapping.xml])){
+            if (mapping.domainProperty){
+              json[mapping.xml].forEach(function(mappingVal){
+                var obj={};
+                obj[mapping.domainProperty]=  mappingVal;
+                domain[mapping.domain].pushObject(obj);
+              });
+            }else{
+              domain[mapping.domain].pushObjects(json[mapping.xml]);
+            }
+          }else{
+            if(mapping.domainProperty){
+              var obj = {};
+              obj[mapping.domainProperty]=  json[mapping.xml];
+              domain[mapping.domain].pushObject(obj);
+            }else{
+              domain[mapping.domain].pushObject(json[mapping.xml]);
+            }
+          }
+        }
+      }else if (mapping.customHandler){
+        if (json[mapping.xml]){
+          mapping.customHandler.handleImport(domain,json[mapping.xml]);
+        }
+      }
+    });
+  }
+});
+var ConfigurationMapper= Ember.Object.extend({
+  hanldeGeneration(node,nodeObj){
+    if (!node || !node.configuration || !node.configuration.property){
+      return;
+    }
+    var props=[];
+    node.configuration.property.forEach(function(config){
+      props.push({name:config.name,value:config.value});
+    });
+    if (props.length>0){
+      var configuration={"property":props};
+      return configuration;
+    }
+  },
+  handleImport(domain,nodeObj){
+    if (!nodeObj.property){
+      return;
+    }
+    var configs=Ember.A([]);
+    domain.configuration={property:configs};
+    if (Ember.isArray(nodeObj.property)){
+      nodeObj.property.forEach(function(prop){
+        var propObj=Ember.Object.create({
+          name: prop.name,
+          value: prop.value
+        });
+        configs.pushObject(propObj);
+      });
+    }else{
+      var propObj=Ember.Object.create({
+        name: nodeObj.property.name,
+        value: nodeObj.property.value
+      });
+      configs.pushObject(propObj);
+    }
+  }
+});
+
+var PrepareMapper= Ember.Object.extend({
+  hanldeGeneration(node,nodeObj){
+    if (!node){
+      return;
+    }
+    if (node.prepare && node.prepare.length>0){
+      node.prepare.sort(function(a,b){
+        if (a.type==="delete"){
+          return -1;
+        }else{
+          return 1;
+        }
+      });
+      var prepareObjs={};
+      nodeObj["prepare"]=prepareObjs;
+      node.prepare.forEach(function(prep){
+        if (!prepareObjs[prep.type]){
+          prepareObjs[prep.type]=[];
+        }
+        prepareObjs[prep.type].push({"_path":prep.path});
+      });
+    }
+  },
+  handleImport(domain,nodeObj){
+    domain.prepare=[];
+    if (nodeObj.delete){
+      this.handlePrepActionInternal(domain.prepare,nodeObj.delete,"delete");
+    }
+    if (nodeObj.mkdir){
+      this.handlePrepActionInternal(domain.prepare,nodeObj.mkdir,"mkdir");
+    }
+
+  },
+  handlePrepActionInternal(prepareDomain,actionObjs,type){
+    if (Ember.isArray(actionObjs)){
+      actionObjs.forEach(function(actionObj){
+        var obj=Ember.Object.create({
+          path: actionObj._path,
+          type: type
+        });
+        prepareDomain.push(obj);
+      });
+    }else{
+      var obj=Ember.Object.create({
+        path: actionObjs._path,
+        type: type
+      });
+      prepareDomain.push(obj);
+    }
+  }
+});
+var SLAMapper= Ember.Object.extend({
+  hanldeGeneration(sla,nodeObj){
+    if (sla){
+      var slaInfo=nodeObj["info"]={};
+      slaInfo["__prefix"]="sla";
+      if (sla.nominalTime){
+        slaInfo["nominal-time"]=sla.nominalTime;
+      }
+      if (sla.shouldStart){
+        slaInfo["should-start"]="${"+sla.shouldStart.time+ "*"+sla.shouldStart.unit+"}";
+      }
+      if (sla.shouldEnd){
+        slaInfo["should-end"]="${"+sla.shouldEnd.time+ "*"+sla.shouldEnd.unit+"}";
+      }
+      if (sla.maxDuration){
+        slaInfo["max-duration"]="${"+sla.maxDuration.time+ "*"+sla.maxDuration.unit+"}";
+      }
+      if (sla.alertEvents){
+        slaInfo["alert-events"]=sla.alertEvents;
+      }
+      if (sla.alertContact){
+        slaInfo["alert-contact"]=sla.alertContact;
+      }
+
+    }
+    return nodeObj;
+  },
+  handleImport(domain,infoJson,key){
+    var sla=domain[key]=SlaInfo.create({});
+    if (infoJson["nominal-time"]){
+      sla.nominalTime=infoJson["nominal-time"];
+    }
+    sla.alertContact=infoJson["alert-contact"];
+    sla.alertEvents=infoJson["alert-events"];
+    this.processTimePeriods(sla,infoJson,"should-start","shouldStart");
+    this.processTimePeriods(sla,infoJson,"should-end","shouldEnd");
+    this.processTimePeriods(sla,infoJson,"max-duration","maxDuration");
+  },
+  processTimePeriods(sla,infoJson,key,domainKey){
+    if (infoJson[key]){
+      var timeParts=this.parseSlaTime(infoJson[key],key);
+      sla[domainKey].time=timeParts[0];
+      sla[domainKey].unit=timeParts[1];
+    }
+  },
+  parseSlaTime(str,key){
+    var timePeriod= str.substring(str.indexOf("{")+1,str.indexOf("}"));
+    return timePeriod.split("*");
+  }
+});
+export {MappingMixin,ConfigurationMapper,PrepareMapper,SLAMapper};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
new file mode 100644
index 0000000..c440b8c
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
@@ -0,0 +1,120 @@
+/*
+*    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 {Node} from '../domain/node';
+import {idGen} from '../domain/id-gen';
+var NodeFactory= Ember.Object.extend({
+  createStartNode(){
+    return this.createNode({id:'node-start', type:'start',name:"Start"});
+  },
+  createEndNode(name){
+    return this.createNode({id:'node-end', type:'end', name:name});
+  },
+  createKillNode(name,message){
+    return this.createNode({id:this.generateNodeId(), type:"kill", name:name,killMessage:message});
+  },
+  createPlaceholderNode(target,errorPath){
+    var placeholderNode= this.createNode({
+      id:this.generateNodeId(), type:"placeholder",
+      name:"placeholder" + this.generateName(),
+      errorPath:errorPath
+    });
+    placeholderNode.addTransitionTo(target);
+    return placeholderNode;
+  },
+  createActionNode(actionType,name){
+    if (!name){
+      name=actionType;
+    }
+    return this.createNode({id:this.generateNodeId(),type:"action",actionType:actionType, name:name});
+  },
+  generateDecisionNodeWithJoinPlaceHolder(target){
+    var id=this.generateNodeId();
+    var decisionNode=this.createEmptyDecisionNode("decision" + this.generateName(),id);
+    var joinPlaceholder = this.createNode({id:"decision_end_"+id, type:"decision_end", name:"decision_end" + this.generateName()});
+    joinPlaceholder.addTransitionTo(target);
+    var leftPlaceholder =this.createPlaceholderNode(joinPlaceholder) ;
+    decisionNode.addTransitionTo(leftPlaceholder,"default");
+    var rightPlaceholder = this.createPlaceholderNode(joinPlaceholder);
+    decisionNode.addTransitionTo(rightPlaceholder);
+    return decisionNode;
+  },
+  generateDecisionNodeWithoutJoinPlaceHolder(target){
+    var decisionNode=this.createEmptyDecisionNode("decision");
+    var leftPlaceholder =this.createPlaceholderNode(target) ;
+    decisionNode.addTransitionTo(leftPlaceholder,"default");
+    if (Constants.globalSetting.useAdditionalPlaceholderFlowForDecision){
+      var rightPlaceholder = this.createPlaceholderNode(target);
+      decisionNode.addTransitionTo(rightPlaceholder);
+    }
+    return decisionNode;
+  },
+  generateDecisionNode(target){
+    if (Constants.globalSetting.useJoinNodeForDecision){
+      return this.generateDecisionNodeWithJoinPlaceHolder(target);
+    }else{
+      return this.generateDecisionNodeWithoutJoinPlaceHolder(target);
+    }
+  },
+  createEmptyDecisionNode(name,id){
+    if (!id){
+      id=this.generateNodeId();
+    }
+    var decisionNode = this.createNode({id:id, type:"decision", name:name});
+    return decisionNode;
+  },
+  createEmptyForkNode(name,id){
+    if (!id){
+      id=this.generateNodeId();
+    }
+    var forkNode = this.createNode({id:id, type:"fork", name:name});
+    return forkNode;
+  },
+  createEmptyJoinNode(name,id){
+    if (!id){
+      id=this.generateNodeId();
+    }
+    var joinNode=this.createNode({id:id, type:"join", name:name});
+    return joinNode;
+  },
+  generateForkNode(target){
+    var forkId=this.generateNodeId();
+    var forkNode= this.createEmptyForkNode("fork" + this.generateName(),forkId);
+    var joinNode= this.createEmptyJoinNode("join" + this.generateName(),"join_"+forkId);
+    joinNode.addTransitionTo(target);
+
+    var leftPlaceholder =this.createPlaceholderNode(joinNode) ;
+    forkNode.addTransitionTo(leftPlaceholder);
+
+    var rightPlaceholder = this.createPlaceholderNode(joinNode);
+    forkNode.addTransitionTo(rightPlaceholder);
+    return forkNode;
+  },
+  createNode(settings){
+    settings.factory=this;
+    return Node.create(settings);
+  },
+  generateNodeId(){
+    return idGen.generateNodeId();
+  },
+  generateName(){
+    return idGen.generateNodeName();
+  }
+});
+export{NodeFactory};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
new file mode 100644
index 0000000..49347d8
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
@@ -0,0 +1,251 @@
+/*
+*    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 {NodeFactory} from '../domain/node-factory';
+import * as actionJobHandler from '../domain/actionjob_hanlder';
+import {SlaInfo} from '../domain/sla-info';
+import {SLAMapper} from "../domain/mapping-utils";
+var ActionTypeResolver=Ember.Object.extend({
+  actionJobHandlerMap:null,
+  validStandardActionProps:["ok","error","info"],
+  init(){
+    var settings={schemaVersions:this.schemaVersions};
+    this.actionJobHandlerMap=new Map();
+    this.actionJobHandlerMap.set("java",actionJobHandler.JavaActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("pig",actionJobHandler.PigActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("hive",actionJobHandler.HiveActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("hive2",actionJobHandler.Hive2ActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("sqoop",actionJobHandler.SqoopActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("shell",actionJobHandler.ShellActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("spark",actionJobHandler.SparkActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("map-reduce",actionJobHandler.MapRedActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("sub-workflow",actionJobHandler.SubWFActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("distcp",actionJobHandler.DistCpJobHandler.create(settings));
+    this.actionJobHandlerMap.set("ssh",actionJobHandler.SshActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("email",actionJobHandler.EmailActionJobHandler.create(settings));
+    this.actionJobHandlerMap.set("fs",actionJobHandler.FSActionJobHandler.create(settings));
+  },
+  getActionType(json){
+    var self=this;
+    var resolvedType=null;
+    var problaleActionsTypes=[];
+    Object.keys(json).forEach(function functionName(key) {
+      if (!self.validStandardActionProps.contains(key) && !key.startsWith("_")){
+        problaleActionsTypes.push(key);
+      }
+    });
+    if (problaleActionsTypes.length===1){
+      return problaleActionsTypes[0];
+    }else{
+      console.error("Invalid Action spec..",json);
+    }
+    return resolvedType;
+  },
+  getActionJobHandler(jobType){
+    return this.actionJobHandlerMap.get(jobType);
+  }
+});
+var NodeHandler=Ember.Object.extend({
+  nodeFactory:NodeFactory.create({}),
+  context : {},
+  setContext(context){
+    this.context = context;
+  },
+  getContext(){
+    return this.context;
+  },
+  hasMany(){
+    return true;
+  },
+  handleNode(node){
+    return {"_name":node.get("name")};
+  },
+
+  handleTransitions(transitions,nodeObj){
+
+  },
+  handleImportNode(type,node){
+  },
+  handleImportTransitions(node,json,nodeMap){
+  }
+});
+var StartNodeHandler= NodeHandler.extend({
+  hasMany(){
+    return false;
+  },
+  handleNode(node){
+    return {};
+  },
+  handleTransitions(transitions,nodeObj){
+    if (transitions.length!==1){
+      this.context.addError({node:nodeObj, message:"Invalid Start Node"});
+    }
+    nodeObj["_to"]=transitions[0].targetNode.getName();
+  },
+  handleImportNode(type,node,workflow){
+    return this.nodeFactory.createStartNode();
+  },
+  handleImportTransitions(node,json,nodeMap){
+    node.addTransitionTo(nodeMap.get(json._to).node);
+  }
+});
+var EndNodeHandler= NodeHandler.extend({
+  hasMany(){
+    return false;
+  },
+  handleImportNode(type,node,workflow){
+    return this.nodeFactory.createEndNode("end");
+  },
+
+});
+var KillNodeHandler= NodeHandler.extend({
+  type:"kill",
+  handleImportNode(type,node,workflow){
+    return this.nodeFactory.createKillNode(node._name,node.message);
+  },
+  handleNode(node){
+    var obj= {"_name":node.get("name")};
+    if (!Ember.isBlank(node.get("killMessage"))){
+      obj["message"]=node.get("killMessage");
+    }
+    return obj;
+  }
+});
+var ActionNodeHandler= NodeHandler.extend({
+  type:"action",
+  actionTypeResolver:null,
+  schemaVersions: null,
+  slaMapper: SLAMapper.create({}),
+  init(){
+  },
+  handleNode(node){
+    var nodeObj=this._super(node);
+    if (node.domain && !Ember.isBlank(node.domain.credentials)){
+      nodeObj._cred=node.domain.credentials;
+    }
+    return nodeObj;
+  },
+  handleSla(domain,nodeObj){
+    if (domain && domain.slaEnabled){
+      return this.slaMapper.hanldeGeneration(domain.slaInfo,nodeObj);
+    }
+  },
+
+  handleTransitions(transitions,nodeObj){
+    transitions.forEach(function(tran){
+      if (!tran.condition){
+        nodeObj["ok"]={"_to":tran.getTargetNode().getName()};
+      }else if (tran.condition="error"){
+        nodeObj["error"]={"_to":tran.getTargetNode().getName()};
+      }
+    });
+  },
+  handleImportNode(type,nodeJson,workflow){
+    var actionType=this.get("actionTypeResolver").getActionType(nodeJson);
+
+    var actionNode = this.nodeFactory.createActionNode(actionType,nodeJson._name);
+    if (actionType===null){
+      console.error("cannot handle unsupported node:"+nodeJson);//TODO error handling...
+      return actionNode;
+    }
+    var actionJobHandler=this.get("actionTypeResolver").getActionJobHandler(actionType);
+    if (!actionJobHandler){
+      console.error("cannot handle unsupported action type:"+actionType+" for "+nodeJson._name);//TODO error handling...
+      return actionNode;
+    }
+    actionJobHandler.handleImport(actionNode,nodeJson[actionType]);
+    if (nodeJson.info && nodeJson.info.__prefix==="sla") {
+      actionNode.domain.slaEnabled=true;
+      this.slaMapper.handleImport(actionNode.domain,nodeJson.info,"slaInfo");
+    }
+    actionNode.domain.credentials=nodeJson._cred;
+    return actionNode;
+  },
+  handleImportTransitions(node,json,nodeMap){
+    node.addTransitionTo(nodeMap.get(json.ok._to).node);
+    if (json.error && json.error._to){
+      node.addTransitionTo(nodeMap.get(json.error._to).node,"error");
+    }
+  }
+});
+var DecisionNodeHandler= NodeHandler.extend({
+  type:"decision",
+  handleTransitions(transitions,nodeObj){
+    var swithCaseObj={"case":[]};
+    nodeObj["switch"]=swithCaseObj;
+    var caseObjects=swithCaseObj["case"];
+    transitions.forEach(function(tran){
+      if (tran.condition!=="default"){
+        caseObjects.push({"_to":tran.getTargetNode().getName(),"__text":tran.condition});
+      }else{
+        swithCaseObj['default']={};
+        swithCaseObj['default']["_to"]=tran.getTargetNode().getName();
+      }
+
+    });
+  },
+  handleImportNode(type,node,workflow){
+    return this.nodeFactory.createEmptyDecisionNode(node._name);
+  },
+  handleImportTransitions(node,json,nodeMap){
+    var defaultPath=json.switch.default._to;
+    node.addTransitionTo(nodeMap.get(defaultPath).node,"default");
+    var cases=[];
+    if (Ember.isArray(json.switch.case)){
+      cases=json.switch.case;
+    }else{
+      cases.push(json.switch.case);
+    }
+    cases.forEach(function(caseExpr){
+      node.addTransitionTo(nodeMap.get(caseExpr._to).node,caseExpr.__text);
+    });
+  }
+});
+var ForkNodeHandler= NodeHandler.extend({
+  type:"fork",
+  handleTransitions(transitions,nodeObj){
+    var pathObjects=[];
+    nodeObj["path"]=pathObjects;
+    transitions.forEach(function(tran){
+      pathObjects.push({"_start":tran.getTargetNode().getName()});
+    });
+  },
+  handleImportNode(type,node,workflow){
+    return this.nodeFactory.createEmptyForkNode(node._name);
+  },
+  handleImportTransitions(node,json,nodeMap){
+    json.path.forEach(function(path){
+      node.addTransitionTo(nodeMap.get(path._start).node);
+    });
+  }
+});
+var JoinNodeHandler= NodeHandler.extend({
+  type:"join",
+  handleTransitions(transitions,nodeObj){
+    transitions.forEach(function(tran){
+      nodeObj["_to"]=tran.getTargetNode().getName();
+    });
+  },
+  handleImportNode(type,node,workflow){
+    return this.nodeFactory.createEmptyJoinNode(node._name);
+  },
+  handleImportTransitions(node,json,nodeMap){
+    node.addTransitionTo(nodeMap.get(json._to).node);
+  }
+});
+export{ActionTypeResolver,NodeHandler,StartNodeHandler,EndNodeHandler,KillNodeHandler,ActionNodeHandler,DecisionNodeHandler,ForkNodeHandler,JoinNodeHandler};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-visitor.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-visitor.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-visitor.js
new file mode 100644
index 0000000..adc020e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-visitor.js
@@ -0,0 +1,38 @@
+/*
+*    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';
+var NodeVisitor = Ember.Object.extend({
+  process(node,callback,context){
+    var visitedNodes=[];
+    return this.visitNode(node,callback,context,visitedNodes);
+  },
+  visitNode(node,callback,context,visitedNodes){
+    if (visitedNodes.contains(node.get("id"))){
+      return;
+    }
+    visitedNodes.push(node.get("id"));
+    callback(node,context);
+    var self=this;
+    if (node.transitions){
+      node.transitions.forEach(function(tran){
+        return self.visitNode(tran.targetNode,callback,context,visitedNodes);
+      });
+    }
+  }
+});
+export {NodeVisitor};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
new file mode 100644
index 0000000..c7ce003
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
@@ -0,0 +1,212 @@
+/*
+*    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 {FindNodeMixin} from '../domain/findnode-mixin';
+import {Transition} from '../domain/transition';
+import Constants from '../utils/constants';
+var Node = Ember.Object.extend(FindNodeMixin,{
+  id:null,
+  name:null,
+  type:null,
+  actionType:null,/*for action nodes*/
+  transitions:null,
+  domain:null,
+  errorNode:null,
+  killnodeName:"",
+  factory:null,
+  errorMsgs:[],
+  init(){
+    this.transitions = [];
+  },
+  onSave(){
+    var self=this;
+    if (this.isDecisionNode()){
+      var i=0;
+      this.get("domain").forEach(function(tran){
+        self.get("transitions")[i].condition=tran.condition;
+        i++;
+      });
+    }else if (this.isActionNode()){
+      var errorNode=this.get("errorNode");
+      if(errorNode && errorNode.id){
+        this.setErrorTransitionTo(errorNode);
+      }
+    }
+  },
+  validateCustom(){
+    this.errorMsgs=[];
+    //TODO Custom validations
+    return this.errorMsgs;
+  },
+  deleteCurrentKillNode(){
+    var self=this;
+    this.get("transitions").forEach(function(tran){
+      if (tran.condition==="error"){
+        self.removeTransition(tran);
+      }
+    });
+  },
+  getNodeDetail(){
+    var domain={};
+    if (this.isDecisionNode()){
+      var flows=[];
+      this.get("transitions").forEach(function(tran){
+        flows.push({condition: tran.condition, targetName: tran.getTargetNode().getName()});
+      });
+      this.set("domain",flows);
+      return this.get("domain");
+    }else if (this.get("domain")){
+      return this.get("domain");
+    }else{
+      return domain;
+    }
+  },
+  getName(){
+    return this.get("name")?this.get("name"):this.get("id");
+  },
+  findTransitionTo(target){
+    var oldTrans = null;
+    var transitions=this.get("transitions");
+    transitions.forEach(function(tran){
+      if (tran.targetNode === target) {
+        oldTrans = tran;
+        return false;
+      }
+    });
+    return oldTrans;
+  },
+  addTransition(transition) {
+    var transitions=this.get("transitions");
+    if (transitions && transitions.indexOf(transition) === -1) {
+      if (transition.condition==="error"){
+        this.set("errorNode",transition.getTargetNode());
+      }
+      transitions.push(transition);
+    }
+  },
+  addTransitionTo(target,condition){
+    var transition = Transition.create({targetNode:target,sourceNode:this,condition:condition});
+    this.addTransition(transition);
+    return transition;
+  },
+  setErrorTransitionTo(target){
+    var errorTrans=this.getErrorTransition();
+    if (!errorTrans){
+      if (target){
+        if (target.isKillNode()){
+          this.addTransitionTo(target,"error");
+        }else{
+          this.addTransitionTo(target,"error");
+        }
+      }
+    }else{
+      errorTrans.set("targetNode",target);
+    }
+  },
+
+  removeTransition(transition){
+    var transitions=this.get("transitions");
+    if (transition && this.transitions.indexOf(transition) > -1) {
+      this.transitions.splice(this.transitions.indexOf(transition), 1);
+    }
+  },
+  hasTransition(){
+    var transitions=this.get("transitions");
+    if (!transitions){
+      return false;
+    }else{
+      return transitions.length>0;
+    }
+  },
+  getErrorTransition(){
+    var errorTrans=null;
+    this.get("transitions").forEach(function(tran){
+      if (tran.condition==="error"){
+        errorTrans=tran;
+        return;
+      }
+    });
+    return errorTrans;
+  },
+  getOkTransitionCount(){
+    var count=0;
+    this.get("transitions").forEach(function(tran){
+      if (tran.condition!=="error"){
+        count++;
+      }
+    });
+    return count;
+  },
+  getDefaultTransitionTarget(){
+    if (this.isForkNode()){
+      return this.findNodeById(this,"join_"+this.get("id"));
+    }
+    var transitions=this.get("transitions");
+    if (transitions.length===0){
+      return this;
+    }else if (transitions.length===1){
+      return transitions[0].targetNode;
+    }
+    var target=transitions[0].targetNode;
+    transitions.forEach(function(tran){
+      if (tran.condition==="default"){
+        target=tran.targetNode;
+      }
+    });
+    if (target.isPlaceholder()){
+      return target.getDefaultTransitionTarget();
+    }
+    return target;
+  },
+  getTargets(){
+    var targets=[];
+    var transitions=this.get("transitions");
+    transitions.forEach(function(tran){
+      targets.push(tran.get("targetNode"));
+    });
+    return targets;
+  },
+  addError(obj){
+    this.errorMsgs.push(obj);
+  },
+  isPlaceholder(){
+    return this.get("type")==="placeholder";
+  },
+  isDecisionNode(){
+    return this.get("type")==="decision";
+  },
+  isForkNode(){
+    return this.get("type")==="fork";
+  },
+  isJoinNode(){
+    return this.get("type")==="join";
+  },
+  isActionNode(){
+    return this.get("type")==="action";
+  },
+  isKillNode(){
+    return this.get("type")==="kill";
+  },
+  isDecisionEnd(){
+    return this.get("type")==="decision_end";
+  },
+  isDefaultKillNode(){
+    return this.isKillNode() && this.get("name")===Constants.defaultKillNodeName;
+  }
+});
+export {Node};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/schema-versions.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/schema-versions.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/schema-versions.js
new file mode 100644
index 0000000..9562ae8
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/schema-versions.js
@@ -0,0 +1,70 @@
+/*
+*    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.Object.extend({
+  actionVersions: Ember.Map.create(),
+  currentActionVersion:Ember.Map.create(),
+  clone : {},
+  createCopy(){
+    this.clone.workflowVersion = this.workflowVersion;
+    this.clone.currentActionVersion = this.currentActionVersion.copy();
+  },
+  rollBack(){
+    this.workflowVersion = this.clone.workflowVersion;
+    this.currentActionVersion = this.clone.currentActionVersion.copy();
+  },
+  init(){
+    this.workflowVersion = "0.5";
+    this.workflowVersions = ["0.5","0.4.5","0.4","0.3","0.2.5","0.2","0.1"];
+    this.actionVersions.set("hive",["0.6","0.5","0.4","0.3","0.2","0.1"]);
+    this.actionVersions.set("hive2",["0.2","0.1"]);
+    this.actionVersions.set("pig",["0.3","0.2","0.1"]);
+    this.actionVersions.set("sqoop",["0.3","0.2","0.1"]);
+    this.actionVersions.set("shell",["0.3","0.2","0.1"]);
+    this.actionVersions.set("spark",["0.2","0.1"]);
+    this.actionVersions.set("distcp",["0.2","0.1"]);
+    this.actionVersions.set("email",["0.2","0.1"]);
+
+    this.currentActionVersion.set("hive","0.6");
+    this.currentActionVersion.set("hive2","0.2");
+    this.currentActionVersion.set("pig","0.3");
+    this.currentActionVersion.set("sqoop","0.3");
+    this.currentActionVersion.set("shell","0.3");
+    this.currentActionVersion.set("spark","0.2");
+    this.currentActionVersion.set("distcp","0.2");
+    this.currentActionVersion.set("email","0.2");
+  },
+  getActionVersions(type){
+    return this.actionVersions.get(type);
+  },
+  getActionVersion(type){
+    return this.currentActionVersion.get(type);
+  },
+  getCurrentWorkflowVersion(){
+    return this.workflowVersion;
+  },
+  getWorkflowVersions(){
+    return this.workflowVersions;
+  },
+  setActionVersion(type, version){
+    this.currentActionVersion.set(type, version);
+  },
+  setCurrentWorkflowVersion(version){
+    return this.workflowVersion = version;
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
new file mode 100644
index 0000000..76dffbd
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
@@ -0,0 +1,59 @@
+/*
+*    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';
+var SlaInfo = Ember.Object.extend(Ember.Copyable,{
+  copy (){
+    var slaInfo = {}
+    for (let key in this) {
+      slaInfo[key] = Ember.copy(this[key]) ;
+    }
+    return slaInfo;
+  },
+  init (){
+    this.nominalTime='';
+    this.shouldStart = {
+      time : '',
+      unit : ''
+    };
+    this.shouldEnd = {
+      time : '',
+      unit : ''
+    };
+    this.maxDuration = {
+      time : '',
+      unit : ''
+    };
+    this.alertEvents = '';
+    this.alertContacts = '';
+  },
+  nominalTime:'',
+  shouldStart : {
+    time : '',
+    unit : ''
+  },
+  shouldEnd : {
+    time : '',
+    unit : ''
+  },
+  maxDuration : {
+    time : '',
+    unit : ''
+  },
+  alertEvents : '',
+  alertContacts : ''
+});
+export {SlaInfo};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
new file mode 100644
index 0000000..70d7a81
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
@@ -0,0 +1,53 @@
+/*
+*    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';
+var Transition = Ember.Object.extend({
+  id:null,
+  sourceNode:null,
+  targetNode:null,
+  type:null,
+  condition:null,
+  errorPath:false,
+  init(){
+
+  },
+  copyAttribs(transition){
+    this.condition=transition.condition;
+  },
+  isOnError(){
+    return this.condition==="error";
+  },
+  isDefaultCasePath(){
+    return this.condition==="default";
+  },
+  getSourceNode(){
+    return this.get("sourceNode");
+  },
+  getTargetNode(skipPlaceholder){
+    var currNode=this.targetNode;
+    if (skipPlaceholder===false){
+      return currNode;
+    }
+    while(currNode.isPlaceholder()){
+      var targets=currNode.getTargets();
+      currNode=targets[0];
+    }
+    return currNode;
+  }
+});
+export {Transition};

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-context.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-context.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-context.js
new file mode 100644
index 0000000..91003c4
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-context.js
@@ -0,0 +1,34 @@
+/*
+*    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';
+var WorkflowContext = Ember.Object.extend({
+  errors : [],
+  addError (error){
+    this.errors.pushObject(error);
+  },
+  getErrors (){
+    return this.errors;
+  },
+  hasErrors (){
+    return this.errors.length > 0;
+  },
+  clearErrors (){
+    this.errors.clear();
+  }
+});
+export{WorkflowContext};


Mime
View raw message