Repository: ambari Updated Branches: refs/heads/branch-embedded-views a30d15738 -> 848545c00
AMBARI-17824. Show existing views under relevant service page as tabs. (Manasi Maheshwari via Jaimin) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/848545c0 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/848545c0 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/848545c0 Branch: refs/heads/branch-embedded-views Commit: 848545c00f8d6c08a736a9ff0e2880c469b831e9 Parents: a30d157 Author: Jaimin Jetly <[email protected]> Authored: Fri Jul 22 18:09:20 2016 -0700 Committer: Jaimin Jetly <[email protected]> Committed: Fri Jul 22 18:09:20 2016 -0700 ---------------------------------------------------------------------- .../internal/ViewResourceProvider.java | 11 +++ .../ambari/server/orm/entities/ViewEntity.java | 23 +++++ .../org/apache/ambari/view/ViewDefinition.java | 8 ++ .../app/controllers/main/charts/heatmap.js | 1 - ambari-web/app/models.js | 1 + ambari-web/app/models/view_instance.js | 89 +++++++++++++++++++ ambari-web/app/routes/main.js | 92 ++++++++++++++++---- ambari-web/app/routes/views.js | 2 +- ambari-web/app/styles/application.less | 3 + ambari-web/app/templates/main/service.hbs | 8 +- ambari-web/app/templates/main/service/item.hbs | 2 +- ambari-web/app/views.js | 1 - ambari-web/app/views/main/service/info/menu.js | 33 +++++-- .../test/views/main/service/info/menu_test.js | 9 +- 14 files changed, 250 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java index 189daef..7586c17 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java @@ -48,6 +48,11 @@ public class ViewResourceProvider extends AbstractAuthorizedResourceProvider { */ public static final String VIEW_NAME_PROPERTY_ID = "ViewInfo/view_name"; + /** + * Service that a view supports in order to show for that Service's quicklinks. + */ + public static final String VIEW_SERVICE = "ViewInfo/service_name"; + /** * The key property ids for a view resource. @@ -63,6 +68,7 @@ public class ViewResourceProvider extends AbstractAuthorizedResourceProvider { private static Set<String> propertyIds = new HashSet<String>(); static { propertyIds.add(VIEW_NAME_PROPERTY_ID); + propertyIds.add(VIEW_SERVICE); } @@ -113,6 +119,11 @@ public class ViewResourceProvider extends AbstractAuthorizedResourceProvider { Resource resource = new ResourceImpl(Resource.Type.View); setResourceProperty(resource, VIEW_NAME_PROPERTY_ID, viewDefinition.getCommonName(), requestedIds); + // Value will eventually come from an xml file that belongs to the view. + // Some views, like the Admin, does not belong to a Service. + // E.g., viewDefinition.getServiceName(); + // For now, just hardcode using a map. + setResourceProperty(resource, VIEW_SERVICE, viewDefinition.getServiceName(), requestedIds); resources.add(resource); } http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java index 74de530..9c746cc 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java @@ -894,4 +894,27 @@ public class ViewEntity implements ViewDefinition { public static String getViewName(String name, String version) { return name + "{" + version + "}"; } + + public String getServiceName() { + // Try + switch (this.getCommonName()) { + case "FILES": + return "HDFS"; + case "HIVE": + return "HIVE"; + case "CAPACITY_SCHEDULER": + return "YARN"; + case "ADMIN_VIEW": + return null; + case "PIG": + return "PIG"; + case "SLIDER": + return "SLIDER"; + case "TEZ": + return "TEZ"; + default: + return "Unknown"; + } + + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java index b9efd74..3fbff09 100644 --- a/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java +++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java @@ -79,6 +79,14 @@ public interface ViewDefinition { */ public String getStatusDetail(); + /** + * Get the service name that may correspond to this view. + * + * @return the service name + */ + public String getServiceName(); + + // ----- ViewStatus enum --------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/controllers/main/charts/heatmap.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/charts/heatmap.js b/ambari-web/app/controllers/main/charts/heatmap.js index 73d1800..e83c26e 100644 --- a/ambari-web/app/controllers/main/charts/heatmap.js +++ b/ambari-web/app/controllers/main/charts/heatmap.js @@ -63,7 +63,6 @@ App.MainChartsHeatmapController = Em.Controller.extend(App.WidgetSectionMixin, { */ activeWidget: Em.computed.alias('widgets.firstObject'), - /** * This function is called from the bound view of the controller */ http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/models.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js index e0168a2..7872d54 100644 --- a/ambari-web/app/models.js +++ b/ambari-web/app/models.js @@ -81,3 +81,4 @@ require('models/configs/objects/service_config_property'); require('models/widget'); require('models/widget_property'); require('models/widget_layout'); +require('models/view_instance'); http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/models/view_instance.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/view_instance.js b/ambari-web/app/models/view_instance.js new file mode 100644 index 0000000..57f49ec --- /dev/null +++ b/ambari-web/app/models/view_instance.js @@ -0,0 +1,89 @@ +/** + * 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. + */ + +var App = require('app'); + +App.ViewInstance = DS.Model.extend({ + id: DS.attr('string'), + name: DS.attr('string'), + displayName: DS.attr('string'), + serviceName: DS.attr('string'), + viewName: DS.attr('string'), + version: DS.attr('string'), + layoutName: DS.attr('string'), + instanceName: function() { + return this.get('name'); + }.property('name') +}); + +App.ViewInstance.FIXTURES = [ + { + id: 'FILES_AUTO_FILES_INSTANCE_1.0.0', + service_name: 'HDFS', + name: 'AUTO_FILES_INSTANCE', + display_name: 'View 1', + view_name: 'FILES', + version: '1.0.0', + layout_name: 'service-tab' + }, + { + id: 'FILES_test1_1.0.0', + service_name: 'HDFS', + name: 'test1', + display_name: 'View 2', + view_name: 'FILES', + version: '1.0.0', + layout_name: 'service-tab' + }, + { + id: 'CAPACITY-SCHEDULER_AUTO_CS_INSTANCE_1.0.0', + service_name: 'YARN', + name : 'AUTO_CS_INSTANCE', + display_name: 'View 1', + view_name : 'CAPACITY-SCHEDULER', + version : '1.0.0', + layout_name: 'service-tab' + }, + { + id: 'HIVE_AUTO_HIVE_INSTANCE_1.0.0', + service_name: 'HIVE', + name : 'AUTO_HIVE_INSTANCE', + display_name: 'View 1', + view_name : 'HIVE', + version : '1.0.0', + layout_name : 'service-tab' + }, + { + id: 'HIVE_AUTO_HIVE_INSTANCE_2.0.0', + service_name: 'HIVE', + name : 'AUTO_HIVE_INSTANCE', + display_name: 'View 2', + view_name : 'HIVE', + version : '2.0.0', + layout_name : 'service-tab' + }, + { + id: 'TEZ_TEZ_CLUSTER_INSTANCE_0.7.0.2.5.0.0-5308', + service_name: 'TEZ', + name : 'TEZ_CLUSTER_INSTANCE', + display_name: 'View 1', + view_name : 'TEZ', + version : '0.7.0.2.5.0.0-5308', + layout_name : 'service-tab' + } +]; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/routes/main.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js index ebf9cb3..6a32bbd 100644 --- a/ambari-web/app/routes/main.js +++ b/ambari-web/app/routes/main.js @@ -116,20 +116,20 @@ module.exports = Em.Route.extend(App.RouterRedirections, { index: Em.Route.extend({ route: '/', enter: function (router) { - Em.run.next(function () { - router.transitionTo('main.dashboard.widgets'); - }); + Em.run.next(function () { + router.transitionTo('main.dashboard.widgets'); + }); } }), goToDashboardView: function (router, event) { - router.transitionTo(event.context); + router.transitionTo(event.context); }, widgets: Em.Route.extend({ route: '/metrics', connectOutlets: function (router, context) { - App.loadTimer.start('Dashboard Metrics Page'); - router.set('mainDashboardController.selectedCategory', 'widgets'); - router.get('mainDashboardController').connectOutlet('mainDashboardWidgets'); + App.loadTimer.start('Dashboard Metrics Page'); + router.set('mainDashboardController.selectedCategory', 'widgets'); + router.get('mainDashboardController').connectOutlet('mainDashboardWidgets'); } }), charts: Em.Route.extend({ @@ -142,23 +142,23 @@ module.exports = Em.Route.extend(App.RouterRedirections, { index: Ember.Route.extend({ route: '/', enter: function (router) { - Em.run.next(function () { - router.transitionTo('heatmap'); - }); + Em.run.next(function () { + router.transitionTo('heatmap'); + }); } }), heatmap: Em.Route.extend({ route: '/heatmap', connectOutlets: function (router, context) { - router.get('mainController').dataLoading().done(function () { - router.get('mainChartsController').connectOutlet('mainChartsHeatmap'); - }); + router.get('mainController').dataLoading().done(function () { + router.get('mainChartsController').connectOutlet('mainChartsHeatmap'); + }); } }), horizon_chart: Em.Route.extend({ route: '/horizon_chart', connectOutlets: function (router, context) { - router.get('mainChartsController').connectOutlet('mainChartsHorizon'); + router.get('mainChartsController').connectOutlet('mainChartsHorizon'); } }), showChart: function (router, event) { @@ -181,7 +181,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { router.get('mainServiceInfoConfigsController').set('preSelectedConfigVersion', event.context); router.transitionTo('main.services.service.configs', App.Service.find(event.context.get('serviceName'))); router.get('mainServiceItemController').set('routeToConfigs', false); - } + }, }), views: require('routes/views'), @@ -524,7 +524,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { route: '/history', connectOutlets: function (router, context) { router.get('mainAdminStackAndUpgradeController').connectOutlet('mainAdminStackUpgradeHistory'); - }, + } }), stackNavigate: function (router, event) { @@ -701,6 +701,57 @@ module.exports = Em.Route.extend(App.RouterRedirections, { router.get('mainServiceItemController').connectOutlet('mainServiceInfoMetrics', item); } }), + + views: require('routes/views'), + menuViews: Em.Route.extend({ + route: '/:view_instance_id', + connectOutlets: function (router, context) { + // find and set content for `mainViewsDetails` and associated controller + var serviceName = App.router.get('mainServiceItemController.content.serviceName'); + var instanceName = context.get('instanceName'); + var viewName = context.get('viewName'); + var version = context.get('version'); + var href = ['/views', viewName, version, instanceName + "/"].join('/'); + var viewPath = this.parseViewPath(window.location.href.slice(window.location.href.indexOf('?'))); + if (viewPath) { + var slicedInstanceName = this._getSlicedInstanceName(instanceName); + if (slicedInstanceName === instanceName) { + viewPath = ''; + } + href = ['/views', viewName, version, slicedInstanceName + "/"].join('/'); + //remove slash from viewPath since href already contains it at the end + if (viewPath.charAt(0) === '/') viewPath = viewPath.slice(1); + } + router.get('mainViewsController').dataLoading().done(function () { + var content = App.router.get('mainViewsController.ambariViews').findProperty('href', href); + if (content) content.set('viewPath', viewPath); + router.get('mainServiceItemController').connectOutlet('mainViewsDetails', content); + }); + }, + /** + * parse the instance name and slice if needed + * + * @returns {string} + * @private + */ + _getSlicedInstanceName: function (instanceName) { + if (instanceName.lastIndexOf('?') > -1) { + return instanceName.slice(0, instanceName.lastIndexOf('?')); + } + + return instanceName; + }, + parseViewPath: function (instanceName) { + var path = ''; + if (instanceName.contains('?')) { + path = instanceName.slice(instanceName.indexOf('?')); + if (path.contains('viewPath')) { + path = decodeURIComponent(path.slice((path.lastIndexOf('?viewPath=') + 10))).replace('&', '?'); + } + } + return path; + } + }), configs: Em.Route.extend({ route: '/configs', connectOutlets: function (router, context) { @@ -755,7 +806,14 @@ module.exports = Em.Route.extend(App.RouterRedirections, { } }), showInfo: function (router, event) { - router.transitionTo(event.context); + var viewInstance = event.context.item; + if (viewInstance) { + var serviceName = viewInstance.get('serviceName'); + App.router.route('/main/services/' + viewInstance.get('serviceName') + '/' + viewInstance.get('id')); + } else { + router.transitionTo(event.context.routing); + } + } }), showService: Em.Router.transitionTo('service'), http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/routes/views.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/routes/views.js b/ambari-web/app/routes/views.js index 1d19279..683d822 100644 --- a/ambari-web/app/routes/views.js +++ b/ambari-web/app/routes/views.js @@ -17,7 +17,7 @@ */ var App = require('app'); - +require('views/main/views_view'); module.exports = Em.Route.extend({ route: '/views', enter: function (router) { http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/styles/application.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less index 200c7be..7e0968e 100644 --- a/ambari-web/app/styles/application.less +++ b/ambari-web/app/styles/application.less @@ -1535,6 +1535,8 @@ a:focus { border-radius: 0; -webkit-border-radius: 0; -moz-border-radius: 0; + z-index: 10; + position: relative; } .dropdown-submenu.submenu-left { &> .dropdown-menu-wrap { @@ -2488,6 +2490,7 @@ a.services-menu-blocks{ margin-top: -53px; position: relative; left: 278px; + z-index: 1; .nav-pills.move { float: right; width:135px; http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/templates/main/service.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/service.hbs b/ambari-web/app/templates/main/service.hbs index 3a4f786..5464556 100644 --- a/ambari-web/app/templates/main/service.hbs +++ b/ambari-web/app/templates/main/service.hbs @@ -17,11 +17,7 @@ }} <div class="row-fluid"> - <div class="services-menu well span2 service-menu-width" style="padding: 8px 0"> - {{view App.MainServiceMenuView}} - {{view App.AllServicesActionView}} - </div> - <div class="span10 summary-width"> - {{outlet}} + <div class="span12 summary-width"> + {{outlet}} </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/templates/main/service/item.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/service/item.hbs b/ambari-web/app/templates/main/service/item.hbs index 43fb3dd..ac596b9 100644 --- a/ambari-web/app/templates/main/service/item.hbs +++ b/ambari-web/app/templates/main/service/item.hbs @@ -17,7 +17,7 @@ }} {{view App.MainServiceInfoMenuView configTabBinding="view.hasConfigTab" heatmapTabBinding="view.hasHeatmapTab"}} - <div class="span3 quick-links-wrapper"> + <div class="span7 quick-links-wrapper"> {{#view App.QuickViewLinks contentBinding="view.service"}} {{#if view.showQuickLinks}} <ul class="nav nav-pills move"> http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/views.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js index 904edb7..caf1460 100644 --- a/ambari-web/app/views.js +++ b/ambari-web/app/views.js @@ -18,7 +18,6 @@ // load all views here - require('views/application'); require('views/common/log_search_ui_link_view'); require('views/common/log_file_search_view'); http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/app/views/main/service/info/menu.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/info/menu.js b/ambari-web/app/views/main/service/info/menu.js index 3533a72..0e78a5c 100644 --- a/ambari-web/app/views/main/service/info/menu.js +++ b/ambari-web/app/views/main/service/info/menu.js @@ -26,7 +26,9 @@ App.MainServiceInfoMenuView = Em.CollectionView.extend({ { label: Em.I18n.t('services.service.info.menu.summary'), id: 'summary-service-tab', - routing: 'summary', + route: { + routing: 'summary' + }, active: "active" } ]; @@ -35,16 +37,32 @@ App.MainServiceInfoMenuView = Em.CollectionView.extend({ menuItems.push({ label: Em.I18n.t('services.service.info.menu.heatmaps'), id: 'heatmap-service-tab', - routing: 'heatmaps' + route: { + routing: 'heatmaps' + } }); } if (this.get('configTab')) { menuItems.push({ label: Em.I18n.t('services.service.info.menu.configs'), id: 'configs-service-tab', - routing: 'configs' + route: { + routing: 'configs' + } }); } + + var serviceName = this.get('parentView.service.serviceName'); + App.ViewInstance.find().filterProperty('serviceName', serviceName).filterProperty('layoutName', 'service-tab').forEach(function(item){ + menuItems.push({ + label: item.get('displayName'), + id: item.get('id'), + route: { + routing: 'menuViews', + item: item + } + }) + }); return menuItems; }.property(), @@ -55,7 +73,12 @@ App.MainServiceInfoMenuView = Em.CollectionView.extend({ activateView: function () { this.get('_childViews').forEach(function(view) { - view.set('active', (document.URL.endsWith(view.get('content.routing')) ? "active" : "")); + if(document.URL.endsWith(view.get('content.route.routing'))){ + view.set('active', (document.URL.endsWith(view.get('content.route.routing')) ? "active" : "")); + } + else { + view.set('active', (document.URL.endsWith(view.get('content.id')) ? "active" : "")); + } }, this); }.observes('App.router.location.lastSetURL'), @@ -66,6 +89,6 @@ App.MainServiceInfoMenuView = Em.CollectionView.extend({ itemViewClass: Em.View.extend({ classNameBindings: ["active"], active: "", - template: Ember.Handlebars.compile('<a {{action showInfo view.content.routing}} {{bindAttr id="view.content.id"}} href="#"> {{unbound view.content.label}}</a>') + template: Ember.Handlebars.compile('<a {{action showInfo view.content.route}} {{bindAttr id="view.content.id"}} href="#"> {{unbound view.content.label}}</a>') }) }); http://git-wip-us.apache.org/repos/asf/ambari/blob/848545c0/ambari-web/test/views/main/service/info/menu_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/main/service/info/menu_test.js b/ambari-web/test/views/main/service/info/menu_test.js index 5caa5b0..35d1111 100644 --- a/ambari-web/test/views/main/service/info/menu_test.js +++ b/ambari-web/test/views/main/service/info/menu_test.js @@ -72,7 +72,14 @@ describe('App.MainServiceInfoMenuView', function () { describe("#activateView()", function() { it("_childViews should be active", function() { view.set('_childViews', [ - Em.Object.create({active: '', content: {routing: 'login'}}) + Em.Object.create({ + active: '', + content: { + route: { + routing: 'login' + } + } + }) ]); view.activateView(); expect(view.get('_childViews')[0].get('active')).to.equal('active');
