You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by kb...@apache.org on 2020/02/21 06:26:32 UTC

[atlas] 01/03: ATLAS-3554 : UI: changes to display/add/update/delete namespace attributes for entities in entity details page

This is an automated email from the ASF dual-hosted git repository.

kbhatt pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit bad7a89e605beca3ca84f9aa38f00a0a09698693
Author: kevalbhatt <kb...@apache.org>
AuthorDate: Fri Feb 14 17:34:53 2020 +0530

    ATLAS-3554 : UI: changes to display/add/update/delete namespace attributes for entities in entity details page
    
    (cherry picked from commit 70ebb22829a4dae8f06031b045f4848a4c22fc05)
---
 dashboardv3/public/css/scss/common.scss            |   1 +
 dashboardv3/public/css/scss/panel.scss             |   2 +-
 dashboardv3/public/js/models/VEntity.js            |  10 +-
 dashboardv3/public/js/modules/Helpers.js           |   6 +
 .../detail_page/DetailPageLayoutView_tmpl.html     |   1 +
 .../entity/EntityNameSpaceItemView_tmpl.html       |  40 +++
 .../templates/entity/EntityNameSpaceView_tmpl.html |  44 +++
 .../entity/EntityUserDefineItemView_tmpl.html      |   2 +-
 dashboardv3/public/js/utils/UrlLinks.js            |   5 +
 .../js/views/detail_page/DetailPageLayoutView.js   |  13 +-
 .../js/views/entity/EntityNameSpaceItemView.js     | 297 +++++++++++++++++++++
 .../public/js/views/entity/EntityNameSpaceView.js  | 251 +++++++++++++++++
 12 files changed, 667 insertions(+), 5 deletions(-)

diff --git a/dashboardv3/public/css/scss/common.scss b/dashboardv3/public/css/scss/common.scss
index aa20b70..016c8bf 100644
--- a/dashboardv3/public/css/scss/common.scss
+++ b/dashboardv3/public/css/scss/common.scss
@@ -248,6 +248,7 @@ pre {
 
         .custom-col-1 {
             width: 39%;
+            margin-bottom: 10px;
         }
 
         .custom-col-2 {
diff --git a/dashboardv3/public/css/scss/panel.scss b/dashboardv3/public/css/scss/panel.scss
index 9d38a3e..66adc26 100644
--- a/dashboardv3/public/css/scss/panel.scss
+++ b/dashboardv3/public/css/scss/panel.scss
@@ -159,9 +159,9 @@
 .panel-default.custom-panel>.panel-actions {
     float: right;
     margin-top: 15px;
+    margin-right: 10px;
 
     button {
-        margin-right: 10px;
         margin-top: -4px;
     }
 }
diff --git a/dashboardv3/public/js/models/VEntity.js b/dashboardv3/public/js/models/VEntity.js
index 84a185f..e9ea1f3 100644
--- a/dashboardv3/public/js/models/VEntity.js
+++ b/dashboardv3/public/js/models/VEntity.js
@@ -107,7 +107,15 @@ define(['require',
         deleteNameSpace: function(options) {
             var url = UrlLinks.nameSpaceUpdateUrl(options.typeName);
             return this.constructor.nonCrudOperation.call(this, url, 'DELETE', options);
-        }
+        },
+        saveNamespaceEntity: function(guid, options) {
+            var url = UrlLinks.entitiesNamespaceApiUrl(guid);
+            options = _.extend({
+                contentType: 'application/json',
+                dataType: 'json'
+            }, options);
+            return this.constructor.nonCrudOperation.call(this, url, 'POST', options);
+        },
     }, {});
     return VEntity;
 });
\ No newline at end of file
diff --git a/dashboardv3/public/js/modules/Helpers.js b/dashboardv3/public/js/modules/Helpers.js
index 2f36a0d..7ef5f25 100644
--- a/dashboardv3/public/js/modules/Helpers.js
+++ b/dashboardv3/public/js/modules/Helpers.js
@@ -89,6 +89,12 @@ define(['require',
             case '>=':
                 return (v1 >= v2) ? options.fn(this) : options.inverse(this);
                 break;
+            case 'isEmpty':
+                return (_.isEmpty(v1)) ? options.fn(this) : options.inverse(this);
+                break;
+            case 'has':
+                return (_.has(v1, v2)) ? options.fn(this) : options.inverse(this);
+                break;
             default:
                 return options.inverse(this);
                 break;
diff --git a/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html b/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
index 7c5a43e..1eb68e3 100644
--- a/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
+++ b/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
@@ -79,6 +79,7 @@
                 <div class="col-md-6">
                     <div id="r_entityUserDefineView"></div>
                     <div id="r_entityLabelDefineView"></div>
+                    <div id="r_entityNameSpaceView"></div>
                 </div>
             </div>
         </div>
diff --git a/dashboardv3/public/js/templates/entity/EntityNameSpaceItemView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityNameSpaceItemView_tmpl.html
new file mode 100644
index 0000000..d030ee6
--- /dev/null
+++ b/dashboardv3/public/js/templates/entity/EntityNameSpaceItemView_tmpl.html
@@ -0,0 +1,40 @@
+<!--
+ * 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.
+-->
+{{#ifCond model "has" "isNew"}}
+<td class="custom-col-1">
+    {{{callmyfunction getNamespaceDroupdown nameSpaceCollection}}}
+</td>
+<td class="custom-col-0"> : </td>
+<td class="custom-col-1" data-id="value">
+    <input type="text" data-key disabled class="form-control">
+</td>
+<td class="custom-col-2 btn-group">
+    <button class="btn btn-default btn-sm" data-id="deleteItem">
+        <i class="fa fa-minus"> </i>
+    </button>
+    <button class="btn btn-default btn-sm" data-id="addItem">
+        <i class="fa fa-plus"> </i>
+    </button>
+</td>
+{{else}}
+<hr />
+<ul class="namespace-tree-parent">
+    <li>{{model.__internal_UI_nameSpaceName}}</li>
+    <li class="namespace-tree-child" data-id="namespaceTreeChild">
+    </li>
+</ul>
+{{/ifCond}}
\ No newline at end of file
diff --git a/dashboardv3/public/js/templates/entity/EntityNameSpaceView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityNameSpaceView_tmpl.html
new file mode 100644
index 0000000..6b622d7
--- /dev/null
+++ b/dashboardv3/public/js/templates/entity/EntityNameSpaceView_tmpl.html
@@ -0,0 +1,44 @@
+<!--
+ * 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.
+-->
+<div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="namespace">
+    <div class="panel-heading" data-toggle="collapse" href="#namespaceCollapse" aria-expanded="false" style="width: 70%;">
+        <h4 class="panel-title">
+            <a>Namespaces</a>
+        </h4>
+        <div class="btn-group pull-left">
+            <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
+        </div>
+    </div>
+    <div class="panel-actions">
+        <button class="btn btn-action btn-sm" title="" data-id="addNameSpace">Add</button>
+        <button class="btn btn-action btn-sm" style="display: none;" data-id="saveNameSpace">Save</button>
+        <button class="btn btn-action btn-sm" style="display: none;" data-id="cancel">Cancel</button>
+    </div>
+    <div id="namespaceCollapse" class="panel-collapse collapse">
+        <div class="panel-body">
+            <div data-id="namespaceTree"></div>
+            <div class="editBox" style="display: none;">
+                <div class="form-group">
+                    <a href="javascript:void(0)" class="btn btn-action btn-sm" data-id="addItem" data-type="addAttrButton">Add New Attribute</a>
+                </div>
+                <table class="custom-table">
+                    <tbody data-id="itemView"></tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html
index 439943d..74dcd91 100644
--- a/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html
+++ b/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html
@@ -26,7 +26,7 @@
             <textarea placeholder="value" data-type="value" data-index={{@index}} class="form-control" class="form-control">{{value}}</textarea>
             <p class="errorMsg"></p>
         </td>
-        <td class="custom-col-2">
+        <td class="custom-col-2 btn-group">
             <button class="btn btn-default btn-sm" title="" data-index={{@index}} data-id="deleteItem">
                 <i class="fa fa-minus"> </i>
             </button>
diff --git a/dashboardv3/public/js/utils/UrlLinks.js b/dashboardv3/public/js/utils/UrlLinks.js
index 6a09d31..2bbe679 100644
--- a/dashboardv3/public/js/utils/UrlLinks.js
+++ b/dashboardv3/public/js/utils/UrlLinks.js
@@ -99,6 +99,11 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require
                 return this.baseUrlV2 + '/entity/bulk/classification';
             }
         },
+        entitiesNamespaceApiUrl: function(guid) {
+            if (guid) {
+                return this.baseUrlV2 + '/entity/guid/' + guid + '/namespaces?isOverwrite=true';
+            }
+        },
         entityCollectionaudit: function(guid) {
             return this.baseUrlV2 + '/entity/' + guid + '/audit';
         },
diff --git a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
index ccb2317..c2511b9 100644
--- a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
+++ b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
@@ -46,7 +46,8 @@ define(['require',
                 RProfileLayoutView: "#r_profileLayoutView",
                 RRelationshipLayoutView: "#r_relationshipLayoutView",
                 REntityUserDefineView: "#r_entityUserDefineView",
-                REntityLabelDefineView: "#r_entityLabelDefineView"
+                REntityLabelDefineView: "#r_entityLabelDefineView",
+                REntityNameSpaceView: "#r_entityNameSpaceView"
             },
             /** ui selector cache */
             ui: {
@@ -120,7 +121,7 @@ define(['require',
              * @constructs
              */
             initialize: function(options) {
-                _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection', 'searchVent'));
+                _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection', 'nameSpaceCollection', 'searchVent'));
                 $('body').addClass("detail-page");
             },
             bindEvents: function() {
@@ -244,6 +245,7 @@ define(['require',
                         enumDefCollection: this.enumDefCollection,
                         classificationDefCollection: this.classificationDefCollection,
                         glossaryCollection: this.glossaryCollection,
+                        nameSpaceCollection: this.nameSpaceCollection,
                         searchVent: this.searchVent,
                         attributeDefs: (function() {
                             return that.getEntityDef(collectionJSON);
@@ -253,6 +255,7 @@ define(['require',
                     this.renderEntityDetailTableLayoutView(obj);
                     this.renderEntityUserDefineView(obj);
                     this.renderEntityLabelDefineView(obj);
+                    this.renderEntityNameSpaceView(obj);
                     this.renderRelationshipLayoutView(obj);
                     this.renderAuditTableLayoutView(obj);
                     this.renderTagTableLayoutView(obj);
@@ -512,6 +515,12 @@ define(['require',
                     that.REntityLabelDefineView.show(new EntityLabelDefineView(obj));
                 });
             },
+            renderEntityNameSpaceView: function(obj) {
+                var that = this;
+                require(['views/entity/EntityNameSpaceView'], function(EntityNameSpaceView) {
+                    that.REntityNameSpaceView.show(new EntityNameSpaceView(obj));
+                });
+            },
             renderTagTableLayoutView: function(obj) {
                 var that = this;
                 require(['views/tag/TagDetailTableLayoutView'], function(TagDetailTableLayoutView) {
diff --git a/dashboardv3/public/js/views/entity/EntityNameSpaceItemView.js b/dashboardv3/public/js/views/entity/EntityNameSpaceItemView.js
new file mode 100644
index 0000000..9982e14
--- /dev/null
+++ b/dashboardv3/public/js/views/entity/EntityNameSpaceItemView.js
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+define(['require',
+    'backbone',
+    'hbs!tmpl/entity/EntityNameSpaceItemView_tmpl',
+    'moment',
+    'daterangepicker'
+], function(require, Backbone, EntityNameSpaceItemViewTmpl, moment) {
+    'use strict';
+
+    return Backbone.Marionette.ItemView.extend({
+        _viewName: 'EntityNameSpaceItemView',
+
+        template: EntityNameSpaceItemViewTmpl,
+
+        templateHelpers: function() {
+            return {
+                editMode: this.editMode,
+                entity: this.entity,
+                getValue: this.getValue.bind(this),
+                getNamespaceDroupdown: this.getNamespaceDroupdown.bind(this),
+                nameSpaceCollection: this.nameSpaceCollection.fullCollection.toJSON(),
+                model: this.model.toJSON()
+            }
+        },
+        tagName: 'tr',
+        className: "custom-tr",
+
+        /** Layout sub regions */
+        regions: {},
+
+        /** ui selector cache */
+        ui: {
+            keyEl: "[data-id='key']",
+            valueEl: "[data-type='value']",
+            addItem: "[data-id='addItem']",
+            deleteItem: "[data-id='deleteItem']",
+            editMode: "[data-id='editMode']"
+        },
+        /** ui events hash */
+        events: function() {
+            var events = {};
+            events["click " + this.ui.deleteItem] = 'onDeleteItem';
+            events["change " + this.ui.keyEl] = 'onAttrChange';
+            return events;
+        },
+
+        /**
+         * intialize a new EntityNameSpaceItemView Layout
+         * @constructs
+         */
+        initialize: function(options) {
+            _.extend(this, options);
+        },
+        onRender: function() {
+            var that = this;
+            this.ui.keyEl.val("");
+            this.ui.keyEl.select2({ placeholder: "Select Attribute" });
+
+            if (this.editMode && (!this.model.has("isNew"))) {
+                this.getEditNamespaceEl();
+            }
+            this.initializeElement();
+            this.bindEvent();
+        },
+        initializeElement: function() {},
+        bindEvent: function() {
+            var that = this;
+            if (this.editMode) {
+                this.listenTo(this.model.collection, 'destroy unset:attr', function() {
+                    if (this.model.has("isNew")) {
+                        this.render();
+                    }
+                });
+                this.listenTo(this.model.collection, 'selected:attr', function(value, model) {
+                    if (model.cid !== this.model.cid && this.model.has("isNew")) {
+                        var $select2el = that.$el.find('.custom-col-1:first-child>select[data-id="key"]');
+                        $select2el.find('option[value="' + value + '"]').remove();
+                        var select2val = $select2el.select2("val");
+                        $select2el.select2({ placeholder: "Select Attribute" });
+                        if (this.model.keys().length <= 2) {
+                            $select2el.val("").trigger("change", true);
+                        }
+                    }
+                });
+                this.$el.off("change", ".custom-col-1[data-id='value']>[data-key]").on("change", ".custom-col-1[data-id='value']>[data-key]", function(e) {
+                    var key = $(this).data("key"),
+                        namespace = $(this).data("namespace"),
+                        typeName = $(this).data("typename"),
+                        multi = $(this).data("multi"),
+                        updateObj = that.model.toJSON();
+                    if (_.isUndefinedNull(updateObj[key])) {
+                        updateObj[key] = { value: null, typeName: typeName };
+                    }
+                    updateObj[key].value = multi ? $(this).select2("val") : e.currentTarget.value;
+                    if (!that.model.has("__internal_UI_nameSpaceName")) {
+                        updateObj["__internal_UI_nameSpaceName"] = namespace;
+                    }
+                    if (typeName === "date") {
+                        updateObj[key].value = new Date(updateObj[key].value).getTime()
+                    }
+                    that.model.set(updateObj);
+                });
+                this.$el.on('keypress', '.select2_only_number .select2-search__field', function() {
+                    $(this).val($(this).val().replace(/[^\d].+/, ""));
+                    if ((event.which < 48 || event.which > 57)) {
+                        event.preventDefault();
+                    }
+                });
+            }
+        },
+        getAttrElement: function(options) {
+            var that = this,
+                returnEL = "N/A";
+            if (options) {
+                var key = options.key,
+                    typeName = options.val.typeName || "",
+                    val = options.val.value,
+                    isMultiValued = typeName && typeName.indexOf("array<") === 0,
+                    namespace = options.namespace,
+                    allowOnlyNum = false;
+                var elType = isMultiValued ? "select" : "input";
+                if (!_.isEmpty(val)) {
+                    val = _.escape(val);
+                }
+                if (typeName === "boolean") {
+                    val = String(val);
+                }
+                if (typeName === "date" && _.isNumber(val)) {
+                    val = moment(val).format("MM/DD/YYYY");
+                }
+                if (typeName.indexOf("string") > -1) {
+                    returnEL = '<' + elType + ' type="text" data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" data-multi="' + isMultiValued + '"  multiple="' + isMultiValued + '" placeholder="Enter String" class="form-control" ' + (!_.isUndefinedNull(val) ? 'value="' + val + '"' : "") + '></' + elType + '>';
+                } else if (typeName === "boolean") {
+                    returnEL = '<select data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" class="form-control"><option value="">--Select Value--</option><option value="true" ' + (!_.isUndefinedNull(val) && val == "true" ? "selected" : "") + '>true</option><option value="false" ' + (!_.isUndefinedNull(val) && val == "false" ? "selected" : "") + '>false</option></select>';
+                } else if (typeName === "date") {
+                    returnEL = '<input type="text" data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" data-type="date" class="form-control" ' + (!_.isUndefinedNull(val) ? 'value="' + val + '"' : "") + '>'
+                    setTimeout(function() {
+                        var dateObj = { "singleDatePicker": true, "showDropdowns": true };
+                        that.$el.find('input[data-type="date"]').daterangepicker(dateObj);
+                    }, 0);
+                } else if (typeName === "byte" || typeName === "short" || typeName.indexOf("int") > -1 || typeName.indexOf("float") > -1 || typeName === "double" || typeName === "long") {
+                    allowOnlyNum = true;
+                    returnEL = '<' + elType + ' data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" type="number" data-multi="' + isMultiValued + '"  multiple="' + isMultiValued + '" placeholder="Enter Number" class="form-control" ' + (!_.isUndefinedNull(val) ? 'value="' + val + '"' : "") + '></' + elType + '>';
+                } else if (typeName) {
+                    var foundEnumType = this.enumDefCollection.fullCollection.find({ name: typeName });
+                    if (foundEnumType) {
+                        var enumOptions = "";
+                        _.forEach(foundEnumType.get("elementDefs"), function(obj) {
+                            enumOptions += '<option value="' + obj.value + '">' + obj.value + '</option>'
+                        });
+                        returnEL = '<select data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '">' + enumOptions + '</select>';
+                    }
+                    setTimeout(function() {
+                        var selectEl = that.$el.find('.custom-col-1[data-id="value"] select[data-key="' + key + '"]');
+                        selectEl.val((val || ""));
+                        selectEl.select2();
+                    }, 0);
+                }
+                if (isMultiValued) {
+                    setTimeout(function() {
+                        var selectEl = that.$el.find('.custom-col-1[data-id="value"] select[data-key="' + key + '"][data-multi="true"]');
+                        var data = val && val.split(",") || [];
+                        if (allowOnlyNum) {
+                            selectEl.parent().addClass("select2_only_number");
+                        }
+                        selectEl.select2({ tags: true, multiple: true, data: data });
+                        selectEl.val(data).trigger("change");
+                    }, 0);
+                }
+            }
+            return returnEL;
+        },
+        onAttrChange: function(e, manual) {
+            var key = e.currentTarget.value.split(":");
+            if (key.length && key.length === 3) {
+                var valEl = $(e.currentTarget).parent().siblings(".custom-col-1"),
+                    hasModalData = this.model.get(key[1]);
+                if (!hasModalData) {
+                    var tempObj = {
+                        "__internal_UI_nameSpaceName": key[0]
+                    };
+                    if (this.model.has("isNew")) {
+                        tempObj["isNew"] = true;
+                    }
+                    tempObj[key[1]] = null;
+                    this.model.clear({ silent: true }).set(tempObj)
+                }
+                valEl.html(this.getAttrElement({ namespace: key[0], key: key[1], val: hasModalData ? hasModalData : { typeName: key[2] } }));
+                if (manual === undefined) {
+                    this.model.collection.trigger("selected:attr", e.currentTarget.value, this.model);
+                }
+            }
+        },
+        getValue: function(value, key, namespaceName) {
+            var typeName = value.typeName,
+                value = value.value;
+            if (typeName === "date") {
+                return moment(value).format("MM/DD/YYYY");
+            } else {
+                return value;
+            }
+        },
+        getNamespaceDroupdown: function(nameSpaceCollection) {
+            var optgroup = "";
+            var that = this;
+            var model = that.model.omit(["isNew", "__internal_UI_nameSpaceName"]),
+                keys = _.keys(model),
+                isSelected = false,
+                selectdVal = null;
+            if (keys.length === 1) {
+                isSelected = true;
+            }
+            _.each(nameSpaceCollection, function(obj) {
+                var options = "";
+                if (obj.attributeDefs.length) {
+                    _.each(obj.attributeDefs, function(attrObj) {
+                        var entityNamespace = that.model.collection.filter({ __internal_UI_nameSpaceName: obj.name }),
+                            hasAttr = false;
+                        if (entityNamespace) {
+                            var found = entityNamespace.find(function(obj) {
+                                return obj.attributes.hasOwnProperty(attrObj.name);
+                            });
+                            if (found) {
+                                hasAttr = true;
+                            }
+                        }
+                        if ((isSelected && keys[0] === attrObj.name) || !(hasAttr) && attrObj.options.applicableEntityTypes.indexOf('"' + that.entity.typeName + '"') > -1) {
+                            var value = obj.name + ":" + attrObj.name + ":" + attrObj.typeName;
+                            if (isSelected && keys[0] === attrObj.name) { selectdVal = value };
+                            options += '<option value="' + value + '">' + attrObj.name + ' (' + _.escape(attrObj.typeName) + ')</option>';
+                        }
+                    });
+                    if (options.length) {
+                        optgroup += '<optgroup label="' + obj.name + '">' + options + '</optgroup>';
+                    }
+                }
+            });
+
+            setTimeout(function() {
+                if (selectdVal) {
+                    that.$el.find('.custom-col-1:first-child>select[data-id="key"]').val(selectdVal).trigger("change", true);
+                } else {
+                    that.$el.find('.custom-col-1:first-child>select[data-id="key"]').val("").trigger("change", true);
+                }
+            }, 0);
+            return '<select data-id="key">' + optgroup + '</select>';
+        },
+        getEditNamespaceEl: function() {
+            var that = this,
+                trs = "";
+            _.each(this.model.attributes, function(val, key) {
+                if (key !== "__internal_UI_nameSpaceName" && key !== "isNew") {
+                    var td = '<td class="custom-col-1" data-key=' + key + '>' + key + '</td><td class="custom-col-0">:</td><td class="custom-col-1" data-id="value">' + that.getAttrElement({ namespace: that.model.get("__internal_UI_nameSpaceName"), key: key, val: val }) + '</td>';
+
+                    td += '<td class="custom-col-2 btn-group">' +
+                        '<button class="btn btn-default btn-sm" data-key="' + key + '" data-id="deleteItem">' +
+                        '<i class="fa fa-times"> </i>' +
+                        '</button></td>';
+                    trs += "<tr>" + td + "</tr>";
+                }
+            })
+            this.$("[data-id='namespaceTreeChild']").html("<table class='custom-table'>" + trs + "</table>");
+        },
+        onDeleteItem: function(e) {
+            var key = $(e.currentTarget).data("key");
+            if (this.model.has(key)) {
+                if (this.model.keys().length === 2) {
+                    this.model.destroy();
+                } else {
+                    this.model.unset(key);
+                    if (!this.model.has("isNew")) {
+                        this.$el.find("tr>td:first-child[data-key='" + key + "']").parent().remove()
+                    }
+                    this.model.collection.trigger("unset:attr");
+                }
+            } else {
+                this.model.destroy();
+            }
+        }
+    });
+});
\ No newline at end of file
diff --git a/dashboardv3/public/js/views/entity/EntityNameSpaceView.js b/dashboardv3/public/js/views/entity/EntityNameSpaceView.js
new file mode 100644
index 0000000..bbec0f7
--- /dev/null
+++ b/dashboardv3/public/js/views/entity/EntityNameSpaceView.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.
+ */
+
+define([
+    "require",
+    "backbone",
+    "hbs!tmpl/entity/EntityNameSpaceView_tmpl",
+    "views/entity/EntityNameSpaceItemView",
+    "models/VEntity",
+    "utils/Utils",
+    "utils/Enums",
+    "utils/Messages",
+    "utils/CommonViewFunction",
+    'moment'
+], function(require, Backbone, EntityNameSpaceViewTmpl, EntityNameSpaceItemView, VEntity, Utils, Enums, Messages, CommonViewFunction, moment) {
+    "use strict";
+
+    return Backbone.Marionette.CompositeView.extend({
+        _viewName: "EntityNameSpaceView",
+        template: EntityNameSpaceViewTmpl,
+        childView: EntityNameSpaceItemView,
+        childViewContainer: "[data-id='itemView']",
+        childViewOptions: function() {
+            return {
+                editMode: this.editMode,
+                entity: this.entity,
+                nameSpaceCollection: this.nameSpaceCollection,
+                enumDefCollection: this.enumDefCollection
+            };
+        },
+        /** ui selector cache */
+        ui: {
+            addItem: "[data-id='addItem']",
+            addNameSpace: "[data-id='addNameSpace']",
+            saveNameSpace: "[data-id='saveNameSpace']",
+            namespaceTree: "[data-id='namespaceTree']",
+            cancel: "[data-id='cancel']"
+        },
+        events: function() {
+            var events = {};
+            events["click " + this.ui.addItem] = 'createNameElement';
+            events["click " + this.ui.addNameSpace] = "onAddNameSpace";
+            events["click " + this.ui.saveNameSpace] = "onSaveNameSpace";
+            events["click " + this.ui.cancel] = "onCancel";
+            return events;
+        },
+        initialize: function(options) {
+            var that = this;
+            _.extend(this, _.pick(options, "entity", "nameSpaceCollection", "enumDefCollection", "guid", "fetchCollection"));
+            this.editMode - false;
+            this.$("editBox").hide();
+            var nameSpaceSet = {};
+            this.treeData = [];
+            this.actualCollection = new Backbone.Collection(
+                _.map(this.entity.namespaceAttributes, function(val, key) {
+                    var foundNameSpace = that.nameSpaceCollection.fullCollection.find({ name: key });
+                    var tempData = {
+                        text: key,
+                        name: key,
+                        parent: "#",
+                        icon: "fa fa-folder-o",
+                        children: []
+                    }
+                    if (foundNameSpace) {
+                        var attributeDefs = foundNameSpace.get("attributeDefs");
+                        _.each(val, function(aVal, aKey) {
+                            var foundAttr = _.find(attributeDefs, function(o) {
+                                return o.name === aKey
+                            });
+                            if (foundAttr) {
+                                var treVal = aKey + " : " + (foundAttr.typeName === "date" ? moment(aVal).format("MM/DD/YYYY") : aVal);
+                                tempData.children.push({
+                                    text: treVal,
+                                    name: treVal,
+                                    icon: "fa fa-file-o",
+                                    children: []
+                                })
+                                val[aKey] = { value: aVal, typeName: foundAttr.typeName };
+                            }
+                        })
+                    }
+                    that.treeData.push(tempData);
+                    return _.extend({}, val, { __internal_UI_nameSpaceName: key });
+                }));
+            this.collection = new Backbone.Collection();
+            this.entityModel = new VEntity();
+        },
+        updateToActualData: function(options) {
+            var silent = options && options.silent || false;
+            this.collection.reset(this.actualCollection.toJSON(), { silent: silent });
+        },
+        onAddNameSpace: function() {
+            this.ui.addNameSpace.hide();
+            this.ui.saveNameSpace.show();
+            this.ui.cancel.show();
+            this.editMode = true;
+            this.ui.namespaceTree.hide();
+            this.$(".editBox").show();
+            this.updateToActualData({ silent: true });
+            if (this.collection.length === 0) {
+                this.createNameElement();
+            } else {
+                this.collection.trigger("reset");
+            }
+            this.panelOpenClose();
+        },
+        onCancel: function() {
+            this.ui.cancel.hide();
+            this.ui.saveNameSpace.hide();
+            this.ui.addNameSpace.show();
+            this.editMode = false;
+            this.ui.namespaceTree.show();
+            this.$(".editBox").hide();
+            this.updateToActualData();
+            this.panelOpenClose();
+        },
+        panelOpenClose: function() {
+            var collection = this.editMode ? this.collection : this.actualCollection;
+            if (collection && collection.length === 0) {
+                this.$el.find(".panel-heading").addClass("collapsed");
+                this.$el.find(".panel-collapse.collapse").removeClass("in");
+                this.ui.addNameSpace.text("Add");
+                this.ui.addNameSpace.attr("data-original-title", "Add");
+            } else {
+                this.ui.addNameSpace.text("Edit");
+                this.ui.addNameSpace.attr("data-original-title", "Edit All");
+                this.$el.find(".panel-heading").removeClass("collapsed");
+                this.$el.find(".panel-collapse.collapse").addClass("in");
+            }
+        },
+        validate: function() {
+            var validation = true;
+            this.$el.find('.custom-col-1[data-id="value"] [data-key]').each(function(el) {
+                var val = $(this).val(),
+                    elIsSelect2 = $(this).hasClass("select2-hidden-accessible");
+                if (_.isString(val)) {
+                    val = val.trim();
+                }
+                if (_.isEmpty(val)) {
+                    if (validation) {
+                        validation = false;
+                    }
+                    if (elIsSelect2) {
+                        $(this).siblings(".select2").find(".select2-selection").attr("style", "border-color : red !important");
+                    } else {
+                        $(this).css("borderColor", "red");
+                    }
+                } else {
+                    if (elIsSelect2) {
+                        $(this).siblings(".select2").find(".select2-selection").attr("style", "");
+                    } else {
+                        $(this).css("borderColor", "");
+                    }
+                }
+            });
+            return validation;
+        },
+        onSaveNameSpace: function() {
+            var that = this;
+            if (!this.validate()) {
+                return;
+            }
+            this.entityModel.saveNamespaceEntity(this.guid, {
+                data: JSON.stringify(this.generateData()),
+                type: "POST",
+                success: function(data) {
+                    Utils.notifySuccess({
+                        content: "One or more namespace attributes" + Messages.getAbbreviationMsg(false, 'editSuccessMessage')
+                    });
+                    that.entity.namespaceAttributes = data;
+                    this.editMode = false;
+                    that.fetchCollection();
+                    that.onCancel();
+                },
+                complete: function(model, response) {
+                    //that.hideLoader();
+                }
+            });
+        },
+        generateData: function() {
+            var finalObj = {};
+            this.collection.forEach(function(model) {
+                if (!model.has("addAttrButton")) {
+                    var nameSpaceName = model.get("__internal_UI_nameSpaceName"),
+                        modelObj = model.toJSON();
+                    _.each(modelObj, function(o, k) {
+                        if (k === "isNew" && k === "__internal_UI_nameSpaceName") {
+                            delete modelObj[k];
+                            return;
+                        }
+                        if (_.isObject(o) && o.value !== undefined) {
+                            modelObj[k] = o.value;
+                        }
+                    })
+                    if (nameSpaceName !== undefined) {
+                        if (finalObj[nameSpaceName]) {
+                            finalObj[nameSpaceName] = _.extend(finalObj[nameSpaceName], modelObj);
+                        } else {
+                            finalObj[nameSpaceName] = modelObj;
+                        }
+                    }
+                }
+            });
+            if (_.isEmpty(finalObj)) {
+                this.actualCollection.forEach(function(model) {
+                    var nameSpaceName = model.get("__internal_UI_nameSpaceName");
+                    if (nameSpaceName) {
+                        finalObj[nameSpaceName] = {};
+                    }
+                })
+            }
+            return finalObj;
+        },
+        createNameElement: function(options) {
+            var modelObj = { isNew: true };
+            this.collection.unshift(modelObj);
+        },
+        generateTree: function() {
+            this.ui.namespaceTree.jstree({
+                plugins: ["core", "sort", "changed", "wholerow", "conditionalselect"],
+                conditionalselect: function(node) {
+                    return false;
+                },
+                state: { opened: true, selected: false },
+                core: {
+                    multiple: false,
+                    data: this.treeData
+                }
+            })
+        },
+        onRender: function() {
+            this.panelOpenClose();
+            this.generateTree();
+        }
+    });
+});
\ No newline at end of file