ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yus...@apache.org
Subject [10/12] ambari git commit: AMBARI-17213. Create ambari workflow designer contrib view. (Venkat Ranganathan via yusaku)
Date Tue, 09 Aug 2016 20:15:03 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/named-properties.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/named-properties.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/named-properties.js
new file mode 100644
index 0000000..067d643
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/named-properties.js
@@ -0,0 +1,32 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations, {
+  initialize : function () {
+    this.sendAction('register', this, this);
+  }.on('init'),
+  validations : {
+    'property.value': {
+      presence: {
+        'if' :'required',
+        'message' : 'Required'
+      }
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/pass-word.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/pass-word.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/pass-word.js
new file mode 100644
index 0000000..c877e44
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/pass-word.js
@@ -0,0 +1,21 @@
+/*
+ *    Licensed to the Apache Software Foundation (ASF) under one or more
+ *    contributor license agreements.  See the NOTICE file distributed with
+ *    this work for additional information regarding copyright ownership.
+ *    The ASF licenses this file to You under the Apache License, Version 2.0
+ *    (the "License"); you may not use this file except in compliance with
+ *    the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/pig-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/pig-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/pig-action.js
new file mode 100644
index 0000000..ee24871
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/pig-action.js
@@ -0,0 +1,76 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations,{
+  setUp : function(){
+    if(this.get('actionModel.jobXml') === undefined){
+      this.set("actionModel.jobXml", Ember.A([]));
+    }
+    if(this.get('actionModel.args') === undefined){
+      this.set("actionModel.args", Ember.A([]));
+    }
+    if(this.get('actionModel.files') === undefined){
+      this.set("actionModel.files", Ember.A([]));
+    }
+    if(this.get('actionModel.archives') === undefined){
+      this.set("actionModel.archives", Ember.A([]));
+    }
+    if(this.get('actionModel.prepare') === undefined){
+      this.set("actionModel.prepare", Ember.A([]));
+    }
+    if(this.get('actionModel.param') === undefined){
+      this.set("actionModel.param", Ember.A([]));
+    }
+    if(this.get('actionModel.configuration') === undefined){
+      this.set("actionModel.configuration",{});
+      this.set("actionModel.configuration.property", Ember.A([]));
+    }
+  }.on('init'),
+  initialize : function(){
+    this.sendAction('register','pigAction', this);
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+  }.on('didInsertElement'),
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  validations : {
+    'actionModel.script': {
+      presence: {
+        'message' : 'You need to provide a value for Script',
+      }
+    }
+  },
+  actions : {
+    openFileBrowser(model, context){
+      if(undefined === context){
+        context = this;
+      }
+      this.set('filePathModel', model);
+      this.sendAction('openFileBrowser', model, context);
+    },
+    register (name, context){
+      this.sendAction('register',name , context);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config-fs.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config-fs.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config-fs.js
new file mode 100644
index 0000000..9437c25
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config-fs.js
@@ -0,0 +1,209 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations, {
+  mkdirORdeleteORtouchz: true,
+  mkdir: 1,
+  delete: 0,
+  touchz: 0,
+  chmod: 0,
+  move: 0,
+  chgrp: 0,
+  multivalued: true,
+  prepareType: 'mkdir',
+  fileBrowser: Ember.inject.service('file-browser'),
+  initialize: function() {
+    this.on('fileSelected', (fileName)=>{
+      var filePathModel = this.get('filePathModel');
+      if(filePathModel && filePathModel.hasOwnProperty("index") && filePathModel.hasOwnProperty("property")){
+        var fileOperation = this.get('fsOps').objectAt(filePathModel.index);
+        var settings = fileOperation.settings;
+        Ember.set(settings, filePathModel.property, fileName);
+        Ember.set(fileOperation, "settings", settings);
+      }else{
+        this.set(this.get('filePathModel'), fileName);
+      }
+    }.bind(this));
+    this.on('bindInputPlaceholder', function() {
+      this.set('addUnboundValue', true);
+    }.bind(this));
+    this.sendAction('register', 'fsOps', this);
+  }.on('init'),
+  bindInputPlaceholder: function() {
+    let type = this.get("prepareType");
+    if (this.validateOperations(type)) {
+      let value = this.get("prepareType");
+      if (value === "chgrp" && this.get('path') && this.get('group')) {
+        this.addPrepare();
+      } else if (value === "move" && this.get('source') && this.get('target')) {
+        this.addPrepare();
+      } else if (value === "chmod" && this.get('path')) {
+        this.addPrepare();
+      } else if (value === "mkdir" || value === "delete" || value === "touchz" && this.get('path')) {
+        this.addPrepare();
+      }
+    }
+  }.on('willDestroyElement'),
+  formPermissions(r, w, e, type){
+    var perm = 0, permObj = {};
+    if(r){
+      perm = perm+4;
+      permObj[type+"read"] = true;
+    }
+    if(w){
+      perm = perm+2;
+      permObj[type+"write"] = true;
+    }
+    if(e){
+      perm = perm+1;
+      permObj[type+"execute"] = true;
+    }
+    permObj[type+"perm"] = perm;
+    return permObj;
+  },
+  addPrepare: function() {
+
+    let value = this.get("prepareType");
+    switch (value) {
+      case "mkdir":
+      case "delete":
+      case "touchz":
+      this.get('fsOps').pushObject({
+        settings: {
+          path: this.get('path')
+        },
+        type: value
+      });
+      break;
+      case "chmod":
+      var oPerm = this.formPermissions(this.get("oread"), this.get("owrite"), this.get("oexecute"), "o");
+      var gPerm = this.formPermissions(this.get("gread"), this.get("gwrite"), this.get("gexecute"), "g");
+      var rPerm = this.formPermissions(this.get("rread"), this.get("rwrite"), this.get("rexecute"), "r");
+      var permissionsObj = {};
+      permissionsObj = $.extend(true, oPerm, gPerm);
+      permissionsObj = $.extend(true, permissionsObj, rPerm);
+      var perm = oPerm.operm + ""+ gPerm.gperm + ""+ rPerm.rperm;
+      this.get('fsOps').pushObject({
+        settings: {
+          path: this.get('path'),
+          permissions: perm,
+          permissionsObj: permissionsObj,
+          recursive: this.get('recursive'),
+          dirfiles: this.get('dirFiles')
+        },
+        type: value
+      });
+      break;
+      case "chgrp":
+      this.get('fsOps').pushObject({
+        settings: {
+          path: this.get('path'),
+          group: this.get('group'),
+          recursive: this.get('recursive'),
+          dirfiles: this.get('dirFiles')
+        },
+        type: value
+      });
+      break;
+      case "move":
+      this.get('fsOps').pushObject({
+        settings: {
+          source: this.get('source'),
+          target: this.get('target')
+        },
+        type: value
+      });
+      break;
+    }
+    this.resetFields();
+  },
+  resetFields: function() {
+    this.set('prepareType', "mkdir");
+    this.set('path', "");
+    this.set('source', "");
+    this.set('target', "");
+    this.set('group', "");
+    this.set('permissions', "");
+    this.set('recursive', false);
+    this.set('dirFiles', false);
+  },
+  toggleAllFields: function() {
+    this.set("mkdir", 0);
+    this.set("delete", 0);
+    this.set("chmod", 0);
+    this.set("touchz", 0);
+    this.set("chmod", 0);
+    this.set("move", 0);
+    this.set("chgrp", 0);
+  },
+  validateOperations: function(type) {
+    var flag = true;
+    switch (type) {
+      case "mkdir":
+      case "delete":
+      case "touchz":
+      if (!this.get('path')) {
+        flag = false;
+      }
+      break;
+      case "chmod":
+      if (!this.get('path')) {
+        flag = false;
+      }
+      break;
+      case "chgrp":
+      if (!this.get('path') || !this.get('group')) {
+        flag = false;
+      }
+      break;
+      case "move":
+      if (!this.get('source') || !this.get('target')) {
+        flag = false;
+      }
+      break;
+    }
+    return flag;
+  },
+  actions: {
+    onPrepareTypeChange(value) {
+      this.set('prepareType', value);
+      this.toggleAllFields();
+      this.set(value, 1);
+      if (value === "mkdir" || value === "delete" || value === "touchz") {
+        this.set("mkdirORdeleteORtouchz", true);
+      } else {
+        this.set("mkdirORdeleteORtouchz", false);
+      }
+    },
+    addPrepare() {
+      this.addPrepare();
+    },
+    deletePrepare(index) {
+      this.get('fsOps').removeAt(index);
+    },
+    openFileBrowser(model) {
+      this.set('filePathModel', model);
+      this.sendAction("openFileBrowser", model, this);
+    },
+    openFileBrowserForListItem(index, property){
+      this.set('filePathModel',{index:index, property:property});
+      this.sendAction("openFileBrowser", this.get('filePathModel'), this);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config.js
new file mode 100644
index 0000000..038428f
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/prepare-config.js
@@ -0,0 +1,66 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations,{
+  multivalued: true,
+  prepareType : 'mkdir',
+  fileBrowser : Ember.inject.service('file-browser'),
+  initialize : function(){
+    this.on('fileSelected',function(fileName){
+      if(!Ember.isBlank(this.get('filePathModel'))){
+        var prepareObj = this.get('prepare').objectAt(this.get('filePathModel'));
+        Ember.set(prepareObj,"path", fileName);
+        this.get('prepare').replace(this.get('filePathModel'), 1, prepareObj);
+      }else{
+        this.set('preparePath', fileName);
+      }
+    }.bind(this));
+    this.on('bindInputPlaceholder',function () {
+      this.set('addUnboundValue', true);
+    }.bind(this));
+    this.sendAction('register', 'prepare', this);
+  }.on('init'),
+  bindInputPlaceholder : function(){
+    if(this.get('addUnboundValue') && this.get('prepareType') && this.get('preparePath')){
+      this.addPrepare();
+    }
+  }.on('willDestroyElement'),
+  addPrepare : function (){
+    this.get('prepare').pushObject({type:this.get('prepareType'),path:this.get('preparePath')});
+    this.set('prepareType', "mkdir");
+    this.set('preparePath', "");
+    this.$('#prepare-type-select').prop('selectedIndex', 0);
+  },
+  actions : {
+    onPrepareTypeChange (value) {
+      this.set('prepareType', value);
+    },
+    addPrepare () {
+      this.addPrepare();
+    },
+    deletePrepare(index){
+      this.get('prepare').removeAt(index);
+    },
+    openFileBrowser(model){
+      this.set('filePathModel', model);
+      this.sendAction("openFileBrowser", model, this);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/search-create-new-bar.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/search-create-new-bar.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/search-create-new-bar.js
new file mode 100644
index 0000000..c5e1849
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/search-create-new-bar.js
@@ -0,0 +1,202 @@
+/*
+ *    Licensed to the Apache Software Foundation (ASF) under one or more
+ *    contributor license agreements.  See the NOTICE file distributed with
+ *    this work for additional information regarding copyright ownership.
+ *    The ASF licenses this file to You under the Apache License, Version 2.0
+ *    (the "License"); you may not use this file except in compliance with
+ *    the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend(Ember.Evented,{
+    filter : {},
+    history: Ember.inject.service(),
+    startDate : '',
+    endDate : '',
+    tags : Ember.A([]),
+    jobTypeChanged : Ember.observer('jobType', function() {
+      this.$('#search-field').tagsinput('removeAll');
+      this.set('startDate','');
+      this.set('endDate','');
+    }),
+    populateFilters : function (){
+      this.set('tags', Ember.A([]));
+      var previousFilters = this.get('history').getSearchParams();
+      if(!previousFilters || !previousFilters.filter){
+        return;
+      }else{
+        var filterArray = previousFilters.filter.split(";");
+        filterArray.forEach(function(value){
+          if (value.length < 1) {
+            return;
+          }
+          var valueArr = value.split("=");
+          if(valueArr[0] !== 'startCreatedTime' && valueArr[0] !== 'endCreatedTime'){
+            this.get('tags').pushObject(valueArr[0] + ":" + valueArr[1]);
+          }else if(valueArr[0] === 'startCreatedTime'){
+            this.set('startDate',valueArr[1]);
+          }else if(valueArr[0] === 'endCreatedTime'){
+            this.set('endDate',valueArr[1]);
+          }
+        }.bind(this));
+      }
+    }.on('init'),
+
+    displayType : Ember.computed('jobType', function() {
+      if(this.get('jobType') === 'wf'){
+          return "Workflows";
+      }else if(this.get('jobType') === 'coords'){
+          return "Coordinators";
+      }
+      else if(this.get('jobType') === 'bundles'){
+          return "Bundles";
+      }
+      return "Workflows";
+    }),
+    initializeDatePickers : function(){
+      this.$('#startDate').datetimepicker({
+        useCurrent: false,
+        showClose : true,
+        defaultDate : this.get('startDate')
+      });
+      this.$('#endDate').datetimepicker({
+          useCurrent: false, //Important! See issue #1075
+          showClose : true,
+            defaultDate : this.get('endDate')
+      });
+      this.$("#startDate").on("dp.change", function (e) {
+          this.$('#endDate').data("DateTimePicker").minDate(e.date);
+          this.filterByDate(e.date,'start');
+      }.bind(this));
+      this.$("#endDate").on("dp.change", function (e) {
+          this.$('#startDate').data("DateTimePicker").maxDate(e.date);
+          this.filterByDate(e.date,'end');
+      }.bind(this));
+    }.on('didInsertElement'),
+    refresh : function(){
+      var self = this;
+      var source = ['Status:RUNNING',
+                    'Status:SUSPENDED',
+                    'Status:SUCCEEDED',
+                    'Status:KILLED',
+                    'Status:FAILED'];
+      var substringMatcher = function(strs) {
+        return function findMatches(q, cb) {
+          var searchTerm =  self.$('#search-field').tagsinput('input').val();
+          var originalLength = strs.length;
+          if(self.get('jobType') === 'wf'){
+            strs.push('Status:PREP');
+          }
+          strs.push('Name:'+ searchTerm);
+          strs.push('User:'+ searchTerm);
+          var newLength = strs.length;
+          var matches, substrRegex;
+          matches = [];
+          substrRegex = new RegExp(q, 'i');
+          strs.forEach(function(str) {
+            if (substrRegex.test(str)) {
+              matches.push(str);
+            }
+          });
+          strs.splice(originalLength, newLength - originalLength);
+          cb(matches);
+        };
+      };
+      this.$('#search-field').tagsinput({
+          typeaheadjs: {
+            name: 'source',
+            source: substringMatcher(source),
+            highlight : true
+          }
+      });
+      this.get('tags').forEach(function(value){
+        this.$('#search-field').tagsinput('add', value);
+      }.bind(this));
+      this.$('#search-field').tagsinput('refresh');
+      this.$('#search-field').on('itemAdded itemRemoved',function(){
+        var searchTerms = this.$('#search-field').tagsinput('items');
+        var filter = searchTerms.map(function(value){
+          var eachTag = value.split(":");
+          return eachTag[0].toLowerCase()+"="+eachTag[1];
+        });
+        if(filter.length > 0){
+          this.filter.tags = filter.join(";");
+        }else {
+          this.filter.tags = [];
+        }
+        this.sendAction('onSearch', { type: this.get('jobType'), filter: this.getAllFilters()});
+      }.bind(this));
+      this.$('#search-field').on('beforeItemAdd',function(event){
+        var tag = event.item.split(":");
+        if(tag.length < 2){
+          event.cancel = true;
+          this.$('#search-field').tagsinput('add', 'Name:'+tag[0]);
+        }
+      }.bind(this));
+    }.on('didInsertElement'),
+
+    filterByDate(date, dateType){
+      var queryParam;
+      if(dateType === 'start'){
+        queryParam = "startCreatedTime";
+      }else{
+        queryParam = "endCreatedTime";
+      }
+      if (date._isAMomentObject) {
+        var dateFilter = queryParam +"="+ date.format("YYYY-MM-DDThh:mm")+'Z';
+        this.filter[queryParam] = dateFilter;
+      } else {
+        delete this.filter[queryParam];
+      }
+      this.sendAction('onSearch', { type: this.get('jobType'), filter: this.getAllFilters() });
+    },
+
+    getAllFilters(){
+      var allFilters = [];
+      Object.keys(this.filter).forEach(function(value){
+        allFilters.push(this.filter[value]);
+      }.bind(this));
+      return allFilters.join(";");
+    },
+
+    actions: {
+        launchDesign() {
+            this.sendAction('onCreate');
+        },
+        search(type) {
+            var filter = this.get('filterValue'),
+                elem = this.$("#" + type + "_btn");
+            this.$(".scope-btn").removeClass("btn-primary");
+            elem.addClass("btn-primary");
+            this.sendAction('onSearch', { type: type, filter: filter });
+        },
+        refresh(){
+          this.sendAction('onSearch', this.get('history').getSearchParams());
+        },
+        showDatePicker(dateType) {
+          if (dateType === 'start') {
+            this.$("#startDate").trigger("dp.show");
+          } else {
+            this.$("#endDate").trigger("dp.show");
+          }
+        },
+        onClear(type) {
+          if (type ==='start' && this.get('startDate') === "") {
+            this.filterByDate("", type);
+          } else if (type ==='start' && this.get('endDate') === "") {
+            this.filterByDate("", type);
+          }
+
+        }
+    }
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/search-table.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/search-table.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/search-table.js
new file mode 100644
index 0000000..714de66
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/search-table.js
@@ -0,0 +1,128 @@
+/*
+*    Licensed to the Apache Software Foundation (ASF) under one or more
+*    contributor license agreements.  See the NOTICE file distributed with
+*    this work for additional information regarding copyright ownership.
+*    The ASF licenses this file to You under the Apache License, Version 2.0
+*    (the "License"); you may not use this file except in compliance with
+*    the License.  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS,
+*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*    See the License for the specific language governing permissions and
+*    limitations under the License.
+*/
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  showBulkAction : false,
+  history: Ember.inject.service(),
+  currentPage : Ember.computed('jobs.start',function(){
+    if(Ember.isBlank(this.get('jobs.start'))){
+      return 1;
+    }
+    var roundedStart = this.get('jobs.start') - this.get('jobs.start') % 10;
+    return (roundedStart / this.get('jobs.pageSize'))+1;
+  }),
+  rendered : function(){
+    this.sendAction('onSearch', this.get('history').getSearchParams());
+  }.on('didInsertElement'),
+  actions: {
+    selectAll() {
+      this.$(".cbox").click();
+      this.$('#ba_buttons').toggleClass('shown');
+    },
+    onAction(params, deferred) {
+      this.sendAction("onAction", params, deferred);
+    },
+    refresh (){
+      this.sendAction("doRefresh");
+    },
+    doBulkAction(action){
+      var filter = '';
+      var deferred = Ember.RSVP.defer();
+      this.$('.cbox:checked').each(function(index, element){
+        filter = filter + "name=" + this.$(element).attr('name')+ ";";
+      }.bind(this));
+      var params = {};
+      this.$('#bulk-action-loader').removeClass('hidden');
+      params.action = action;
+      params.filter = filter;
+      params.jobType = this.get('jobs.jobTypeValue');
+      params.start = this.get('jobs.start');
+      params.len = this.get('jobs.pageSize');
+      this.sendAction('onBulkAction', params, deferred);
+      deferred.promise.then(function(){
+        this.sendAction("doRefresh");
+        this.$('#bulk-action-loader').addClass('hidden');
+        this.set('showBulkAction', false);
+      }.bind(this), function(){
+        this.$('#bulk-action-loader').addClass('hidden');
+      }.bind(this));
+    },
+    page: function (page) {
+      var size = this.get('jobs.pageSize'),
+      jobType = this.get('jobs.jobTypeValue'),
+      filter = this.get('jobs.filterValue'),
+      start = (size * (page - 1) + 1);
+      start = start || 1;
+      this.sendAction("onSearch", { type: jobType, start: start, filter: filter });
+    },
+    prev: function (page) {
+      page = page - 1;
+      var size = this.get('jobs.pageSize'),
+      jobType = this.get('jobs.jobTypeValue'),
+      filter = this.get('jobs.filterValue'),
+      start = (size * (page - 1) + 1);
+
+      if (page >= 0) {
+        start = start || 1;
+        this.sendAction("onSearch", { type: jobType, start: start, filter: filter });
+      }
+    },
+    next: function (page) {
+      page = page + 1;
+      var size = this.get('jobs.pageSize'),
+      jobType = this.get('jobs.jobTypeValue'),
+      filter = this.get('jobs.filterValue'),
+      total = this.get('jobs.totalValue'),
+      start = (size * (page - 1) + 1);
+      if (start < total) {
+        start = start || 1;
+        this.sendAction("onSearch", { type: jobType, start: start, filter: filter });
+      }
+      this.sendAction("onSearch", { type: jobType, start: start, filter: filter });
+    },
+    showJobDetails : function(jobId){
+      this.sendAction('onShowJobDetails',{type:this.get('jobs.jobTypeValue'), id:jobId});
+    },
+    rowSelected : function(){
+      if(this.$('.cbox:checked').length > 0){
+        this.set('showBulkAction', true);
+        var status = [];
+        this.$('.cbox:checked').each((index, element)=>{
+          status.push(this.$(element).attr('data-status'));
+        }.bind(this));
+        var isSame = status.every(function(value, idx, array){
+          return idx === 0 || value === array[idx - 1];
+        });
+        if(isSame && status[0] === 'SUSPENDED'){
+          this.set('toggleResume', 'enabled');
+          this.set('toggleSuspend', 'disabled');
+        }else if(isSame && status[0] === 'RUNNING'){
+          this.set('toggleSuspend', 'enabled');
+          this.set('toggleResume', 'disabled');
+        }else{
+          this.set('toggleKill', 'enabled');
+          this.set('toggleSuspend', 'disabled');
+          this.set('toggleResume', 'disabled');
+        }
+      }else{
+        this.set('showBulkAction', false);
+      }
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/shell-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/shell-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/shell-action.js
new file mode 100644
index 0000000..a1445dc
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/shell-action.js
@@ -0,0 +1,74 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations,{
+  initialize : function(){
+    this.sendAction('register','shellAction', this);
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+    if(this.get('actionModel.jobXml') === undefined){
+      this.set("actionModel.jobXml", Ember.A([]));
+    }
+    if(this.get('actionModel.args') === undefined){
+      this.set("actionModel.args", Ember.A([]));
+    }
+    if(this.get('actionModel.envVar') === undefined){
+      this.set("actionModel.envVar", Ember.A([]));
+    }
+    if(this.get('actionModel.files') === undefined){
+      this.set("actionModel.files", Ember.A([]));
+    }
+    if(this.get('actionModel.archives') === undefined){
+      this.set("actionModel.archives", Ember.A([]));
+    }
+    if(this.get('actionModel.prepare') === undefined){
+      this.set("actionModel.prepare", Ember.A([]));
+    }
+    if(this.get('actionModel.configuration') === undefined){
+      this.set("actionModel.configuration",{});
+      this.set("actionModel.configuration.property", Ember.A([]));
+    }
+  }.on('didInsertElement'),
+  validations : {
+    'actionModel.exec': {
+      presence: {
+        'message' : 'You need to provide a value for Exec'
+      }
+    }
+  },
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  actions : {
+    openFileBrowser(model, context){
+      if(undefined === context){
+        context = this;
+      }
+      this.set('filePathModel', model);
+      this.sendAction('openFileBrowser', model, context);
+    },
+    register (name, context){
+      this.sendAction('register',name , context);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/sla-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/sla-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/sla-info.js
new file mode 100644
index 0000000..a1bad85
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/sla-info.js
@@ -0,0 +1,147 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations, {
+  alertEvents: Ember.A([]),
+  timeUnitOptions : Ember.A([]),
+  nominalTime : '',
+  initialize : function(){
+    this.set('alertEvents', Ember.A([]));
+    this.get('alertEvents').pushObject({eventType:'start_miss', alertEnabled:false, displayName :'Start Miss'});
+    this.get('alertEvents').pushObject({eventType:'end_miss', alertEnabled:false, displayName : 'End Miss'});
+    this.get('alertEvents').pushObject({eventType:'duration_miss', alertEnabled:false, displayName:'Duration Miss'});
+
+    Ember.addObserver(this, 'alertEvents.@each.alertEnabled', this, this.alertEventsObserver);
+
+    this.set('timeUnitOptions',Ember.A([]));
+    this.get('timeUnitOptions').pushObject({value:'',displayName:'Select'});
+    this.get('timeUnitOptions').pushObject({value:'MINUTES',displayName:'Minutes'});
+    this.get('timeUnitOptions').pushObject({value:'HOURS',displayName:'Hours'});
+    this.get('timeUnitOptions').pushObject({value:'DAYS',displayName:'Days'});
+
+    if(this.get('slaInfo.alertEvents')){
+      var alertsFor = this.get('slaInfo.alertEvents').split(",");
+      alertsFor.forEach((alert)=>{
+        Ember.set(this.get('alertEvents').findBy('eventType', alert),'alertEnabled', true);
+      });
+    }
+    if(this.get('slaEnabled') === undefined){
+      this.set('slaEnabled', false);
+    }
+    Ember.addObserver(this, 'slaEnabled', this, this.slaObserver);
+    if(this.get('slaInfo.nominalTime')){
+      var date = new Date(this.get('slaInfo.nominalTime'));
+      if(date && !isNaN(date.getTime())){
+        var utcDate = new Date(date.getTime() + date.getTimezoneOffset()*60*1000);
+        this.set('nominalTime', moment(utcDate).format("MM/DD/YYYY hh:mm A"));
+      }
+    }
+    Ember.addObserver(this, 'nominalTime', this, this.nominalTimeObserver);
+  }.on('init'),
+  alertEventsObserver : function(){
+    var alerts = this.get('alertEvents').filterBy('alertEnabled',true).mapBy('eventType');
+    this.set('slaInfo.alertEvents', alerts.join());
+  },
+  onDestroy : function(){
+    Ember.removeObserver(this, 'alertEvents.@each.alertEnabled', this, this.alertEventsObserver);
+    Ember.removeObserver(this, 'slaEnabled', this, this.slaObserver);
+    Ember.removeObserver(this, 'nominalTime', this, this.nominalTimeObserver);
+  }.on('willDestroyElement'),
+  elementsInserted : function() {
+    this.$('#nominalTime').datetimepicker({
+      useCurrent: false,
+      showClose : true,
+      defaultDate : this.get('slaInfo.nominalTime')
+    });
+    this.sendAction('register','slaInfo', this);
+    if(this.get('slaEnabled')){
+      this.$('#slaCollapse').collapse('show');
+    }
+  }.on('didInsertElement'),
+  nominalTimeObserver : function(){
+    var date = new Date(this.get('nominalTime'));
+    this.set('slaInfo.nominalTime',moment(date).format("YYYY-MM-DDTHH:mm")+'Z');
+  },
+  shouldEnd : Ember.computed.alias('slaInfo.shouldEnd'),
+  shouldStart : Ember.computed.alias('slaInfo.shouldStart'),
+  maxDuration : Ember.computed.alias('slaInfo.maxDuration'),
+  validations : {
+    'nominalTime': {
+      presence: {
+        'if': 'slaEnabled',
+        'message' : 'Required',
+      }
+    },
+    'shouldEnd.time': {
+      presence: {
+        'if': 'slaEnabled',
+        'message' : 'Required',
+      },
+      numericality: {
+        'if': 'slaEnabled',
+        onlyInteger: true,
+        greaterThan: 0,
+        'message' : 'Number Only'
+      }
+    },
+    'shouldStart.time': {
+      numericality: {
+        'if': 'slaEnabled',
+        allowBlank :true,
+        onlyInteger: true,
+        greaterThan: 0,
+        message : 'Number Only'
+      }
+    },
+    'maxDuration.time': {
+      numericality: {
+        'if': 'slaEnabled',
+        allowBlank :true,
+        onlyInteger: true,
+        greaterThan: 0,
+        message : 'Number Only'
+      }
+    },
+    'shouldStart.unit': {
+      presence: {
+        'if': 'shouldStart.time',
+        'message' : 'Required',
+      }
+    },
+    'shouldEnd.unit': {
+      presence: {
+        'if': 'slaEnabled',
+        'message' : 'Required',
+      }
+    },
+    'maxDuration.unit': {
+      presence: {
+        'if': 'maxDuration.time',
+        'message' : 'Required',
+      }
+    }
+  },
+  slaObserver : function(){
+    if(this.get('slaEnabled')){
+      this.$('#slaCollapse').collapse('show');
+    }else{
+      this.$('#slaCollapse').collapse('hide');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/spark-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/spark-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/spark-action.js
new file mode 100644
index 0000000..4cb74d8
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/spark-action.js
@@ -0,0 +1,119 @@
+/*
+*    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 from 'ember-validations';
+import Constants from '../utils/constants';
+
+export default Ember.Component.extend(EmberValidations,{
+  setup : function(){
+    if(this.get('actionModel.jobXml') === undefined){
+      this.set("actionModel.jobXml", Ember.A([]));
+    }
+    if(this.get('actionModel.args') === undefined){
+      this.set("actionModel.args", Ember.A([]));
+    }
+    if(this.get('actionModel.files') === undefined){
+      this.set("actionModel.files", Ember.A([]));
+    }
+    if(this.get('actionModel.archives') === undefined){
+      this.set("actionModel.archives", Ember.A([]));
+    }
+    if(this.get('actionModel.prepare') === undefined){
+      this.set("actionModel.prepare", Ember.A([]));
+    }
+    if(this.get('actionModel.configuration') === undefined){
+      this.set("actionModel.configuration",{});
+      this.set("actionModel.configuration.property", Ember.A([]));
+    }
+    this.set('mastersList',Ember.copy(Constants.sparkMasterList));
+    this.sendAction('register','sparkAction', this);
+  }.on('init'),
+  initialize : function(){
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+  }.on('didInsertElement'),
+  rendered : function(){
+    if(this.get('actionModel.master')){
+      var master = Constants.sparkMasterList.findBy('value',this.get('actionModel.master'));
+      if(master){
+        this.$("input[name=master][value=" + this.get('actionModel.master') + "]").prop('checked','checked');
+        this.set('disableCustomMaster','disabled');
+      }else{
+        this.$("input[name=master][value=other]").prop('checked','checked');
+        this.set('customMaster',Ember.copy(this.get('actionModel.master')));
+        this.set('disableCustomMaster', false);
+      }
+    }
+  }.on('didInsertElement'),
+  jarObserver : Ember.observer('actionModel.jar',function(){
+    var isJar =  this.get('actionModel.jar') && this.get('actionModel.jar').endsWith('.jar');
+    this.set('isJar', isJar);
+    if(!isJar){
+      this.set('actionModel.class', undefined);
+    }
+  }),
+  validations : {
+    'actionModel.master': {
+      presence: {
+        'message' : 'You need to provide a value for Runs on (Master)'
+      }
+    },
+    'actionModel.jar': {
+      presence: {
+        'message' : 'You need to provide a value for Application'
+      },
+      format : {
+        'with' : /\.jar$|\.py$/i,
+        'message' : 'You need to provide a .jar or .py file'
+      }
+    },
+    'actionModel.sparkName': {
+      presence: {
+        'message' : 'You need to provide a value for Name'
+      }
+    }
+  },
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  actions : {
+    openFileBrowser(model, context){
+      if(undefined === context){
+        context = this;
+      }
+      this.set('filePathModel', model);
+      this.sendAction('openFileBrowser', model, context);
+    },
+    register (name, context){
+      this.sendAction('register',name , context);
+    },
+    onMasterChange (elt){
+      var value = this.$(elt).val();
+      if(value !== 'other'){
+        this.set('actionModel.master',value);
+        this.set('customMaster','');
+        this.set('disableCustomMaster','disabled');
+      }else{
+        this.set('disableCustomMaster', false);
+      }
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/sqoop-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/sqoop-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/sqoop-action.js
new file mode 100644
index 0000000..847cccd
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/sqoop-action.js
@@ -0,0 +1,85 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations,{
+  sqoopSendType : Ember.observer('isArg',function(){
+    if(this.get('isArg')){
+      this.set("actionModel.command", undefined);
+    }else{
+      this.set("actionModel.args",  Ember.A([]));
+    }
+  }),
+  initialize : function(){
+    this.sendAction('register','sqoopAction', this);
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+    if(this.get('actionModel.jobXml') === undefined){
+      this.set("actionModel.jobXml", Ember.A([]));
+    }
+    if(this.get('actionModel.args') === undefined && !this.get('actionModel.command')){
+      this.set("actionModel.args", Ember.A([]));
+      this.set('isArg', false);
+    }else if(this.get('actionModel.args') && this.get('actionModel.args').length > 0){
+      this.set('isArg', true);
+    }else{
+      this.set('isArg', false);
+    }
+    if(this.get('actionModel.files') === undefined){
+      this.set("actionModel.files", Ember.A([]));
+    }
+    if(this.get('actionModel.archives') === undefined){
+      this.set("actionModel.archives", Ember.A([]));
+    }
+    if(this.get('actionModel.prepare') === undefined){
+      this.set("actionModel.prepare", Ember.A([]));
+    }
+    if(this.get('actionModel.configuration') === undefined){
+      this.set("actionModel.configuration",{});
+      this.set("actionModel.configuration.property", Ember.A([]));
+    }
+  }.on('init'),
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  validations : {
+  },
+  actions : {
+    openFileBrowser(model, context){
+      if(undefined === context){
+        context = this;
+      }
+      this.set('filePathModel', model);
+      this.sendAction('openFileBrowser', model, context);
+    },
+    register (name, context){
+      this.sendAction('register',name , context);
+    },
+    onSendTypeChange(value){
+      if(value === "arg"){
+        this.set('isArg',true);
+      }else{
+        this.set('isArg',false);
+      }
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/ssh-action.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/ssh-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/ssh-action.js
new file mode 100644
index 0000000..3ae1f12
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/ssh-action.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';
+import EmberValidations from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations, {
+  fileBrowser : Ember.inject.service('file-browser'),
+  javaOptsObserver : Ember.observer('isSingle',function(){
+    if(this.get('isSingle')){
+      this.set("actionModel.arg", Ember.A([]));
+    }else{
+      this.set("actionModel.args", Ember.A([]));
+    }
+  }),
+  setUp : function(){
+    if(this.get('actionModel.args') === undefined){
+      this.set("actionModel.args", Ember.A([]));
+    }
+    if(this.get('actionModel.arg') === undefined){
+      this.set("actionModel.arg", Ember.A([]));
+    }
+    if(this.get('actionModel.arg') === undefined && !this.get('actionModel.args')){
+      this.set("actionModel.arg", Ember.A([]));
+      this.set('isSingle', false);
+    }else if(this.get('actionModel.arg') === undefined && this.get('actionModel.args')){
+      this.set('isSingle', true);
+    }else{
+      this.set('isSingle', false);
+    }
+  }.on('init'),
+  initialize : function(){
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+    this.sendAction('register','sshAction', this);
+  }.on('didInsertElement'),
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  validations : {
+    'actionModel.host': {
+      presence: {
+        'message' : 'You need to provide a value for host',
+      }
+    },
+    'actionModel.command': {
+      presence: {
+        'message' : 'You need to provide a value for command',
+      }
+    }
+  },
+  actions : {
+    openFileBrowser(model, context){
+      if(undefined === context){
+        context = this;
+      }
+      this.set('filePathModel', model);
+      this.sendAction('openFileBrowser', model, context);
+    },
+    register (name, context){
+      this.sendAction('register',name , context);
+    },
+    onJavaOptChange(value){
+      if(value === "single"){
+        this.set('isSingle',true);
+      }else{
+        this.set('isSingle',false);
+      }
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/sub-workflow.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/sub-workflow.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/sub-workflow.js
new file mode 100644
index 0000000..50ac5bd
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/sub-workflow.js
@@ -0,0 +1,58 @@
+/*
+*    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 from 'ember-validations';
+
+export default Ember.Component.extend(EmberValidations,{
+  setUp : function(){
+    if(this.get('actionModel.configuration') === undefined){
+      this.set("actionModel.configuration",{});
+      this.set("actionModel.configuration.property", Ember.A([]));
+    }
+  }.on('init'),
+  initialize : function(){
+    this.sendAction('register','hiveAction', this);
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+  }.on('didInsertElement'),
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  validations : {
+    'actionModel.appPath': {
+      presence: {
+        'message' : 'You need to provide a value for app path'
+      }
+    }
+  },
+  actions : {
+    openFileBrowser(model, context){
+      if(undefined === context){
+        context = this;
+      }
+      this.set('filePathModel', model);
+      this.sendAction('openFileBrowser', model, context);
+    },
+    register (name, context){
+      this.sendAction('register',name , context);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/transition-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/transition-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/transition-config.js
new file mode 100644
index 0000000..ccf69de
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/transition-config.js
@@ -0,0 +1,67 @@
+/*
+*    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';
+import {FindNodeMixin} from '../domain/findnode-mixin';
+
+export default Ember.Component.extend(FindNodeMixin, EmberValidations, {
+  selectedKillNode : '',
+  initialize : function(){
+    this.set('descendantNodes',this.getDesendantNodes(this.get('currentNode')));
+    this.set('okToNode', this.getOKToNode(this.get('currentNode')));
+    this.sendAction('register','transition', this);
+    if(Ember.isBlank(this.get('transition.errorNode.name'))){
+      this.set('transition.errorNode', this.get('killNodes').objectAt(0));
+    }
+  }.on('init'),
+  //Work-around : Issue in ember-validations framework
+  errorNode : Ember.computed.alias('transition.errorNode'),
+  validations : {
+    'errorNode.name': {
+      inline : validator(function() {
+        if(!this.get('transition.errorNode.name') || this.get('transition.errorNode.name') === ""){
+          return "You need to provide an error-to transition";
+        }
+      })
+    }
+  },
+  actions : {
+    onSelectChange (value){
+      this.set('selectedKillNode', value);
+      if(this.get('selectedKillNode') === 'createNew'){
+        this.set('transition.errorNode.name', "");
+        this.set('transition.errorNode.message', "");
+        this.set('transition.errorNode.isNew', true);
+      }else if(value === ""){
+        this.set('transition.errorNode', null);
+      }else{
+        this.set('transition.errorNode.isNew',false);
+        var node = this.get('descendantNodes').findBy('name',value);
+        if(node){
+          this.set('transition.errorNode', node);
+        }else{
+          node = this.get('killNodes').findBy('name',value);
+          this.set('transition.errorNode', node);
+        }
+      }
+    },
+    okNodeHandler (value){
+
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/version-settings.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/version-settings.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/version-settings.js
new file mode 100644
index 0000000..9103314
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/version-settings.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';
+import Constants from '../utils/constants';
+
+export default Ember.Component.extend({
+  initialize : function(){
+    this.set('currentWorkflowVersion', this.get('schemaVersions').getCurrentWorkflowVersion());
+    this.set('workflowSchemaVersions', this.get('schemaVersions').getWorkflowVersions());
+    this.get('schemaVersions').createCopy();
+    this.set('actionSchemaVersions', Constants.actions);
+    var actionVersions = Ember.A([]);
+    Object.keys(this.get('actionSchemaVersions')).forEach((key)=>{
+      var action = this.get('actionSchemaVersions')[key];
+      if(action.supportsSchema){
+        actionVersions.push({name:action.name, supporedVersions :this.get('schemaVersions').getActionVersions(action.name),selectedVersion:this.get('schemaVersions').getActionVersion(action.name)});
+      }
+    });
+    this.set('actionVersions',actionVersions);
+  }.on('init'),
+  WorkflowVersionObserver : Ember.observer('currentWorkflowVersion',function(){
+    this.get('schemaVersions').setCurrentWorkflowVersion(this.get('currentWorkflowVersion'));
+  }),
+  rendered : function(){
+    this.$('#version-settings-dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#version-settings-dialog').modal('show');
+    this.$('#version-settings-dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('showVersionSettings', false);
+    }.bind(this));
+  }.on('didInsertElement'),
+  actions : {
+    versionChanged : function(actionName, version){
+      this.get('schemaVersions').setActionVersion(actionName, version);
+    },
+    save (){
+      this.$('#version-settings-dialog').modal('hide');
+    },
+    cancel (){
+      this.get('schemaVersions').rollBack();
+      this.$('#version-settings-dialog').modal('hide');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js
new file mode 100644
index 0000000..502fe50
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-action-editor.js
@@ -0,0 +1,190 @@
+/*
+*    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 from 'ember-validations';
+import Constants from '../utils/constants';
+import {SlaInfo} from '../domain/sla-info';
+
+export default Ember.Component.extend(EmberValidations, Ember.Evented,{
+  actionIcons : {
+    "hive": "server",
+    "hive2": "server",
+    "pig": "product-hunt",
+    "sqoop": "database",
+    "hdfs": "copy",
+    "java": "code",
+    "shell": "terminal",
+    "distcp": "clone",
+    "map-reduce": "cubes",
+    "spark": "star",
+    "ssh": "terminal",
+    "sub-workflow":"share-alt-square",
+    "stream": "exchange",
+    "email": "envelope",
+    "fs":"folder-o"
+  },
+  clonedActionModel : {},
+  showingFileBrowser : false,
+  childComponents : new Map(),
+  isActionNode : Ember.computed('nodeType',function(){
+    if(this.get('nodeType') === 'action'){
+      return true;
+    }else{
+      return false;
+    }
+  }),
+  type : Ember.computed('nodeType','actionType',function(){
+    if(this.get('nodeType') === 'action'){
+      return this.get('actionType');
+    }else if(this.get('nodeType') === 'decision' || this.get('nodeType') === 'kill'){
+      return  this.get('nodeType');
+    }
+  }),
+  icon : Ember.computed('actionIcons', 'actionType',function(){
+    return this.get('actionIcons')[this.get('actionType')];
+  }),
+  saveClicked : false,
+  fileBrowser : Ember.inject.service('file-browser'),
+  onDestroy : function(){
+    this.set('transition',{});
+    this.get('childComponents').clear();
+  }.on('willDestroyElement'),
+  setUp : function () {
+    var errorNode = Ember.Object.extend(Ember.Copyable).create({
+      name : "",
+      isNew : false,
+      message : ""
+    });
+    var errorNodeOfCurrentNode = this.get('currentNode').get('errorNode');
+    if(errorNodeOfCurrentNode){
+      errorNode.set('name', errorNodeOfCurrentNode.get('name'));
+      errorNode.set('message', errorNodeOfCurrentNode.get('killMessage'));
+    }
+    var transition = Ember.Object.extend(Ember.Copyable).create({
+      errorNode : errorNode
+    });
+    this.set('transition',transition);
+    if (Ember.isBlank(this.get("actionModel.jobTracker"))){
+      this.set('actionModel.jobTracker',Constants.rmDefaultValue);
+    }
+    if (Ember.isBlank(this.get("actionModel.nameNode"))){
+      this.set('actionModel.nameNode','${nameNode}');
+    }
+    if(this.get('nodeType') === 'action' && this.get('actionModel.slaInfo') === undefined){
+      this.set('actionModel.slaInfo', SlaInfo.create({}));
+    }
+  }.on('init'),
+  initialize : function(){
+    this.$('#action_properties_dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#action_properties_dialog').modal('show');
+    this.$('#action_properties_dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('closeActionEditor', this.get('saveClicked'));
+    }.bind(this));
+    this.get('fileBrowser').on('fileBrowserOpened',function(context){
+      this.get('fileBrowser').setContext(context);
+    }.bind(this));
+    this.on('fileSelected',function(fileName){
+      this.set(this.get('filePathModel'), fileName);
+    }.bind(this));
+  }.on('didInsertElement'),
+  observeError :function(){
+    if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){
+      this.$('#collapseOne').collapse('show');
+    }
+  }.on('didUpdate'),
+  validateChildrenComponents(){
+    var validationPromises = [];
+    var deferred = Ember.RSVP.defer();
+    if(this.get('childComponents').size === 0){
+      deferred.resolve(true);
+    }else{
+      this.get('childComponents').forEach((childComponent)=>{
+        if(!childComponent.validations){
+          return;
+        }
+        var validationDeferred = Ember.RSVP.defer();
+        childComponent.validate().then(()=>{
+          validationDeferred.resolve();
+        }).catch((e)=>{
+          validationDeferred.reject(e);
+        });
+        validationPromises.push(validationDeferred.promise);
+      });
+      Ember.RSVP.Promise.all(validationPromises).then(function(){
+        deferred.resolve(true);
+      }).catch(function(e){
+        deferred.reject(e);
+      });
+    }
+    return deferred;
+  },
+  processMultivaluedComponents(){
+    this.get('childComponents').forEach((childComponent)=>{
+      if(childComponent.get('multivalued')){
+        childComponent.trigger('bindInputPlaceholder');
+      }
+    });
+  },
+  processStaticProps(){
+    this.get('childComponents').forEach((childComponent)=>{
+      if(childComponent.get('hasStaticProps')){
+        childComponent.get('staticProps').forEach((property)=>{
+          this.get(property.belongsTo).push({name:property.name,value:property.value});
+        });
+      }
+    });
+  },
+  actions : {
+    closeEditor (){
+      this.sendAction('close');
+    },
+    save () {
+      var isFormValid = this.validateChildrenComponents();
+      isFormValid.promise.then(function(){
+        this.validate().then(function(){
+          this.processMultivaluedComponents();
+          this.processStaticProps();
+          this.$('#action_properties_dialog').modal('hide');
+          this.sendAction('addKillNode', this.get('transition.errorNode'));
+          this.set('saveClicked', true);
+        }.bind(this)).catch(function(e){
+        }.bind(this));
+      }.bind(this)).catch(function (e) {
+      });
+
+    },
+    openFileBrowser(model, context){
+      if(!context){
+        context = this;
+      }
+      this.get('fileBrowser').trigger('fileBrowserOpened',context);
+      this.set('filePathModel', model);
+      this.set('showingFileBrowser',true);
+    },
+    closeFileBrowser(){
+      this.get('fileBrowser').getContext().trigger('fileSelected', this.get('filePath'));
+      this.set("showingFileBrowser",false);
+    },
+    registerChild (name, context){
+      this.get('childComponents').set(name, context);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js
new file mode 100644
index 0000000..95a7f08
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-actions.js
@@ -0,0 +1,28 @@
+/*
+*    Licensed to the Apache Software Foundation (ASF) under one or more
+*    contributor license agreements.  See the NOTICE file distributed with
+*    this work for additional information regarding copyright ownership.
+*    The ASF licenses this file to You under the Apache License, Version 2.0
+*    (the "License"); you may not use this file except in compliance with
+*    the License.  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS,
+*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*    See the License for the specific language governing permissions and
+*    limitations under the License.
+*/
+
+import Ember from 'ember';
+export default Ember.Component.extend({
+  actions : {
+    addAction : function(type){
+      this.$(".dr_action").css("background-color", "#fff");
+      this.$("[data-type="+type+"]").css("background-color", "#538EC0");
+      this.$(this.get('element')).popover('hide');
+      this.sendAction("addNode", type);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-config.js
new file mode 100644
index 0000000..5919f6d
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-config.js
@@ -0,0 +1,228 @@
+/*
+*    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';
+export default Ember.Component.extend({
+  systemConfigs : Ember.A([]),
+  initialize :function(){
+    this.set("configPropsExists",this.get("workflowSubmitConfigs").props.size>0);
+    var workflowProps =[];
+    var workflowParams = this.get("workflowSubmitConfigs").params;
+    if(workflowParams && workflowParams.configuration && workflowParams.configuration.property){
+      workflowParams.configuration.property.forEach((param)=>{
+        if(param && !param.value){
+          var prop= Ember.Object.create({
+            name: param.name,
+            value: null,
+            isRequired : true
+          });
+          workflowProps.push(prop);
+        }
+      });
+    }
+    this.get("workflowSubmitConfigs").props.forEach(function(value) {
+      if (value!=="${nameNode}" && value!==Constants.rmDefaultValue){
+        var propName = value.trim().substring(2, value.length-1);
+        var isRequired = true;
+        if(workflowParams && workflowParams.configuration && workflowParams.configuration.property){
+          var param = workflowParams.configuration.property.findBy('name', propName);
+          if(param && param.value){
+            isRequired = false;
+          }else {
+            isRequired = true;
+          }
+        }
+        var prop= Ember.Object.create({
+          name: propName,
+          value: null,
+          isRequired : isRequired
+        });
+        workflowProps.push(prop);
+      }
+    });
+    this.set("configMap",workflowProps);
+    this.set("workflowXml",this.get("workflowSubmitConfigs").xml);
+    this.set('systemConfigs', Ember.A([]));
+    this.get('systemConfigs').pushObjects([
+      {displayName: 'Run on submit',name : 'runOnSubmit', value: false},
+      {displayName: 'Use system lib path', name :'useSystemLibPath', value:true},
+      {displayName: 'Rerun on Failure', name : 'rerunOnFailure', value:true}
+    ]);
+    this.set('filePath', Ember.copy(this.get('workflowFilePath')));
+  }.on('init'),
+  rendered : function(){
+    this.$("#configureWorkfowModal").on('hidden.bs.modal', function () {
+      this.sendAction('closeWorkflowSubmitConfigs');
+    }.bind(this));
+    this.$("#configureWorkfowModal").modal("show");
+  }.on('didInsertElement'),
+  showingFileBrowser: false,
+  workflowXml:"",
+  workflowName: "",
+  overwriteWorkflowPath: false,
+  configMap : Ember.A([]),
+  configPropsExists: false,
+  savingInProgress: false,
+  alertType: "",
+  alertMessage:"",
+  alertDetails:"",
+  filePath : "",
+  showNotification(data){
+    if (!data){
+      return;
+    }
+    if (data.type==="success"){
+      this.set("alertType","success");
+    }
+    if (data.type==="error"){
+      this.set("alertType","danger");
+    }
+    this.set("alertDetails",data.details);
+    this.set("alertMessage",data.message);
+  },
+  submitWorkflow(){
+    var self=this;
+    this.set('workflowFilePath', Ember.copy(this.get('filePath')));
+    var url = Ember.ENV.API_URL + "/submitWorkflow?app.path=" +this.get("filePath")+"&overwrite="+this.get("overwriteWorkflowPath");
+    if (this.get("filePath").trim() === ""){//TODO later proper validations.
+      self.showNotification({
+        "type": "error",
+        "message": "Workflow File Path cannot be empty"
+      });
+      return;
+    }
+    var submitConfigs=this.get("configMap");
+    var missingConfig=false;
+    submitConfigs.forEach(function(item) {
+      if (item.isRequired && (!item || !item.value || item.value==="")){
+        missingConfig = true;
+      }else if(!item.isRequired && (!item || !item.value || item.value==="")){
+        return;
+      }else{
+        url = url + "&config." + item.name + "=" + item.value;
+      }
+    }, this);
+    this.get('systemConfigs').forEach((config)=>{
+      url = url + "&oozieconfig." + config.name + "=" + config.value;
+    });
+    if ( this.get("workflowSubmitConfigs").props.has("${resourceManager}")){
+      url= url+"&resourceManager=useDefault";
+    }
+    if (missingConfig){
+      self.showNotification({
+        "type": "error",
+        "message": "You need to fill all the mandatory job properties."
+      });
+      return;
+    }
+
+    this.set("savingInProgress",true);
+    Ember.$.ajax({
+      url: url,
+      method: "POST",
+      dataType: "text",
+      contentType: "text/plain;charset=utf-8",
+      beforeSend: function(request) {
+        request.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+        request.setRequestHeader("X-Requested-By", "workflow-designer");
+      },
+      data: this.get("workflowXml"),
+      success: function(response) {
+        var result=JSON.parse(response);
+        this.showNotification({
+          "type": "success",
+          "message": "Workflow saved.",
+          "details": "Job id :"+result.id
+        });
+        this.set("savingInProgress",false);
+        var runOnSubmit = this.get('systemConfigs').findBy('name','runOnSubmit');
+        if(runOnSubmit.value){
+          this.startJob(result.id);
+        }
+      }.bind(this),
+      error: function(response) {
+        self.set("savingInProgress",false);
+        self.showNotification({
+          "type": "error",
+          "message": "Error occurred while saving workflow.",
+          "details": this.getParsedErrorResponse(response)
+        });
+      }.bind(this)
+    });
+  },
+  startJob (jobId){
+    this.set('startingInProgress', true);
+    var url = [Ember.ENV.API_URL,
+      "/v2/job/", jobId, "?action=", 'start','&user.name=oozie'
+    ].join("");
+    Ember.$.ajax({
+      url: url,
+      method: 'PUT',
+      beforeSend: function (xhr) {
+        xhr.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+        xhr.setRequestHeader("X-Requested-By", "Ambari");
+      }
+    }).done(function(){
+      this.set('startingInProgress', false);
+      this.showNotification({
+        "type": "success",
+        "message": "Workflow Started",
+        "details": jobId
+      });
+    }.bind(this)).fail(function(response){
+      this.set('startingInProgress', false);
+      this.showNotification({
+        "type": "error",
+        "message": "Error occurred while starting workflow.",
+        "details": this.getParsedErrorResponse(response)
+      });
+    }.bind(this));
+  },
+  getParsedErrorResponse (response){
+    var detail;
+    if (response.responseText && response.responseText.charAt(0)==="{"){
+      var jsonResp=JSON.parse(response.responseText);
+      if (jsonResp.status==="workflow.oozie.error"){
+        detail="Oozie error. Please check the workflow.";
+      }else{
+        detail=jsonResp.message;
+      }
+    }else{
+      detail=response;
+    }
+    return detail;
+  },
+  actions: {
+    selectWorflowFile(){
+      this.set("showingFileBrowser",true);
+    },
+    closeFileBrowser(){
+      this.set("showingFileBrowser",false);
+    },
+    save(){
+      this.submitWorkflow();
+      return false;
+    },
+    previewXml(){
+      this.set("showingPreview",true);
+    },
+    closePreview(){
+      this.set("showingPreview",false);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-credentials.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-credentials.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-credentials.js
new file mode 100644
index 0000000..ae24770
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-credentials.js
@@ -0,0 +1,92 @@
+/*
+*    Licensed to the Apache Software Foundation (ASF) under one or more
+*    contributor license agreements.  See the NOTICE file distributed with
+*    this work for additional information regarding copyright ownership.
+*    The ASF licenses this file to You under the Apache License, Version 2.0
+*    (the "License"); you may not use this file except in compliance with
+*    the License.  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS,
+*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*    See the License for the specific language governing permissions and
+*    limitations under the License.
+*/
+import Ember from 'ember';
+
+export default Ember.Component.extend(Ember.Evented, {
+  credentialsList : Ember.A([]),
+  credentialsInfo : {},
+  childComponents : new Map(),
+  initialize : function(){
+    this.get('credentialsList').clear();
+    this.get('childComponents').clear();
+    this.set('credentialsList', Ember.copy(this.get('workflowCredentials')));
+  }.on('init'),
+  rendered : function(){
+    this.$('#workflow_credentials_dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#workflow_credentials_dialog').modal('show');
+    this.$('#workflow_credentials_dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('showCredentials', false);
+    }.bind(this));
+  }.on('didInsertElement'),
+  processMultivaluedComponents(){
+    this.get('childComponents').forEach((childComponent)=>{
+      if(childComponent.get('multivalued')){
+        childComponent.trigger('bindInputPlaceholder');
+      }
+    });
+  },
+  validateChildrenComponents(){
+    var validationPromises = [];
+    var deferred = Ember.RSVP.defer();
+    if(this.get('childComponents').size === 0){
+      deferred.resolve(true);
+    }else{
+      this.get('childComponents').forEach((childComponent)=>{
+        if(!childComponent.validations){
+          return;
+        }
+        var validationDeferred = Ember.RSVP.defer();
+        childComponent.validate().then(()=>{
+          validationDeferred.resolve();
+        }).catch((e)=>{
+          validationDeferred.reject(e);
+        });
+        validationPromises.push(validationDeferred.promise);
+      });
+      Ember.RSVP.Promise.all(validationPromises).then(function(){
+        deferred.resolve(true);
+      }).catch(function(e){
+        deferred.reject(e);
+      });
+    }
+    return deferred;
+  },
+  actions : {
+    register(component, context){
+      this.get('childComponents').set(component, context);
+    },
+    addCredentials (credentialsInfo){
+      this.get('credentialsList').pushObject(credentialsInfo);
+    },
+    deleteCredentials(name){
+      var credentials = this.get('credentialsList').findBy('name', name);
+      this.get('credentialsList').removeObject(credentials);
+    },
+    saveCredentials (){
+      var isFormValid = this.validateChildrenComponents();
+      isFormValid.promise.then(function(){
+        this.processMultivaluedComponents();
+        this.set('workflowCredentials', Ember.copy(this.get('credentialsList')));
+        this.$('#workflow_credentials_dialog').modal('hide');
+      }.bind(this)).catch(function (e) {
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-job-details.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-job-details.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-job-details.js
new file mode 100644
index 0000000..4873a31
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-job-details.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.Component.extend({
+  dagUrl:Ember.computed('model.id',function(){
+    return Ember.ENV.API_URL+'/getDag?jobid='+this.get('model.id');
+  }),
+  actions :{
+    getJobLog (params) {
+      this.sendAction('getJobLog', params);
+    },
+    getActionDetails(action){
+      this.sendAction('getActionDetails',action);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/c7742e7f/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-node.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-node.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-node.js
new file mode 100644
index 0000000..e51135e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/workflow-node.js
@@ -0,0 +1,91 @@
+/*
+*    Licensed to the Apache Software Foundation (ASF) under one or more
+*    contributor license agreements.  See the NOTICE file distributed with
+*    this work for additional information regarding copyright ownership.
+*    The ASF licenses this file to You under the Apache License, Version 2.0
+*    (the "License"); you may not use this file except in compliance with
+*    the License.  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS,
+*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*    See the License for the specific language governing permissions and
+*    limitations under the License.
+*/
+
+import Ember from 'ember';
+
+export default Ember.Component.extend(Ember.Evented,{
+  actionIcons : {
+    "hive": "server",
+    "hive2": "server",
+    "pig": "product-hunt",
+    "sqoop": "database",
+    "hdfs": "copy",
+    "java": "code",
+    "shell": "terminal",
+    "distcp": "clone",
+    "map-reduce": "cubes",
+    "spark": "star",
+    "ssh": "terminal",
+    "sub-workflow":"share-alt-square",
+    "stream": "exchange",
+    "email": "envelope",
+    "fs":"folder-o"
+  },
+  icon : Ember.computed('actionIcons',function(){
+    return this.get('actionIcons')[this.get('node.actionType')];
+  }),
+  nodeSpecificClasses : Ember.computed('node.type',function(){
+    if(this.get('node.type') === 'start'){
+      return "start";
+    }else if(this.get('node.type') === 'end'){
+      return "end";
+    }else if(this.get('node.type') === 'kill'){
+      return "kill";
+    }else if(this.get('node.type') === 'decision'){
+      return "decision_node";
+    }else if(this.get('node.type') === 'decision_end'){
+      return "decision_end";
+    }else if(this.get('node.type') === 'fork'){
+      return "fa fa-sitemap fork";
+    }else if(this.get('node.type') === 'placeholder'){
+      return "placeholder-node";
+    }else if(this.get('node.type') === 'join'){
+      return "fa fa-sitemap fa-rotate-180 control_flow_node join";
+    }else if(this.get('node.type') === 'action'){
+      return "action-node";
+    }
+  }),
+  rendered : function(){
+    if(this.get('node.type') === 'action') {
+      this.$('input[name="actionName"]').focus();
+      this.$('input[name="actionName"]').select();
+    }
+  }.on('didInsertElement'),
+  nameChanged : function(){
+    this.sendAction("onNameChange");
+  }.observes('node.name'),
+  actions : {
+    registerAddBranchAction(component){
+      this.set("addBranchListener",component);
+    },
+    openEditor (){
+      this.sendAction("openEditor", this.get('node'));
+    },
+    showAddBranch(){
+      this.get("addBranchListener").trigger("showBranchOptions");
+    },
+    addBranch(){
+      this.sendAction("addBranch", this.get('node'));
+    },
+    deleteNode(){
+      this.sendAction("deleteNode", this.get('node'));
+    },
+    addDecisionBranch(settings){
+      this.sendAction("addDecisionBranch",settings);
+    }
+  }
+});


Mime
View raw message