AMBARI-22168 Move service metrics to separate tab. (atkach)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/6eb273e1 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/6eb273e1 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/6eb273e1 Branch: refs/heads/trunk Commit: 6eb273e19a81773c27f235631c54a3e142277f08 Parents: e83c86d Author: Andrii Tkach <[email protected]> Authored: Mon Oct 9 14:11:36 2017 +0300 Committer: Andrii Tkach <[email protected]> Committed: Mon Oct 9 15:01:28 2017 +0300 ---------------------------------------------------------------------- ambari-web/app/assets/test/tests.js | 2 + ambari-web/app/controllers.js | 1 + .../app/controllers/main/service/info/metric.js | 468 +++++++++++++++++++ .../controllers/main/service/info/summary.js | 449 +----------------- .../service/widgets/create/wizard_controller.js | 2 +- ambari-web/app/messages.js | 1 + .../app/styles/enhanced_service_dashboard.less | 26 +- .../app/templates/main/service/info/metrics.hbs | 104 +++++ .../app/templates/main/service/info/summary.hbs | 84 ---- ambari-web/app/templates/main/service/item.hbs | 5 +- ambari-web/app/views.js | 1 + ambari-web/app/views/main/service/info/menu.js | 7 + .../app/views/main/service/info/metrics_view.js | 290 ++++++++++++ .../app/views/main/service/info/summary.js | 315 ++----------- ambari-web/app/views/main/service/item.js | 6 + .../main/service/info/metric_test.js | 110 +++++ .../main/service/info/summary_test.js | 76 --- .../main/service/info/metrics_view_test.js | 334 +++++++++++++ .../views/main/service/info/summary_test.js | 281 +---------- 19 files changed, 1400 insertions(+), 1162 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/assets/test/tests.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js index 03b4657..7c636d4 100644 --- a/ambari-web/app/assets/test/tests.js +++ b/ambari-web/app/assets/test/tests.js @@ -125,6 +125,7 @@ var files = [ 'test/controllers/main/service/item_test', 'test/controllers/main/service/info/config_test', 'test/controllers/main/service/info/summary_test', + 'test/controllers/main/service/info/metric_test', 'test/controllers/main/service_test', 'test/controllers/main/admin_test', 'test/controllers/main/views_controller_test', @@ -340,6 +341,7 @@ var files = [ 'test/views/main/service/service_test', 'test/views/main/service/info/config_test', 'test/views/main/service/info/summary_test', + 'test/views/main/service/info/metrics_view_test', 'test/views/main/service/info/menu_test', 'test/views/main/service/info/component_list_view_test', 'test/views/main/service/info/metrics/ambari_metrics/regionserver_base_test', http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js index 81e5eb7..f7d77be 100644 --- a/ambari-web/app/controllers.js +++ b/ambari-web/app/controllers.js @@ -142,6 +142,7 @@ require('controllers/main/charts'); require('controllers/main/charts/heatmap_metrics/heatmap_metric'); require('controllers/main/charts/heatmap'); require('controllers/main/service/info/heatmap'); +require('controllers/main/service/info/metric'); require('controllers/main/views_controller'); require('controllers/main/views/details_controller'); require('controllers/wizard/step0_controller'); http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers/main/service/info/metric.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/info/metric.js b/ambari-web/app/controllers/main/service/info/metric.js new file mode 100644 index 0000000..9dfc32c --- /dev/null +++ b/ambari-web/app/controllers/main/service/info/metric.js @@ -0,0 +1,468 @@ +/** + * 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.MainServiceInfoMetricsController = Em.Controller.extend(App.WidgetSectionMixin, { + name: 'mainServiceInfoMetricsController', + + layoutNameSuffix: "_dashboard", + + sectionNameSuffix: "_SUMMARY", + + /** + * Some widget has type `GRAPH` + * + * @type {boolean} + */ + someWidgetGraphExists: Em.computed.someBy('widgets', 'widgetType', 'GRAPH'), + + /** + * @type {boolean} + */ + showTimeRangeControl: Em.computed.or('!isServiceWithEnhancedWidgets', 'someWidgetGraphExists'), + + /** + * @type {boolean} + */ + isWidgetLayoutsLoaded: false, + + /** + * @type {boolean} + */ + isAllSharedWidgetsLoaded: false, + + /** + * @type {boolean} + */ + isMineWidgetsLoaded: false, + + /** + * load widget layouts across all users in CLUSTER scope + * @returns {$.ajax} + */ + loadWidgetLayouts: function () { + this.set('isWidgetLayoutsLoaded', false); + return App.ajax.send({ + name: 'widgets.layouts.get', + sender: this, + data: { + sectionName: this.get('sectionName') + }, + success: 'loadWidgetLayoutsSuccessCallback' + }); + }, + + loadWidgetLayoutsSuccessCallback: function (data) { + App.widgetLayoutMapper.map(data); + this.set('isWidgetLayoutsLoaded', true); + }, + + + /** + * load all shared widgets to show on widget browser + * @returns {$.ajax} + */ + loadAllSharedWidgets: function () { + this.set('isAllSharedWidgetsLoaded', false); + return App.ajax.send({ + name: 'widgets.all.shared.get', + sender: this, + success: 'loadAllSharedWidgetsSuccessCallback' + }); + }, + + /** + * success callback of <code>loadAllSharedWidgets</code> + * @param {object|null} data + */ + loadAllSharedWidgetsSuccessCallback: function (data) { + var widgetIds = this.get('widgets').mapProperty('id'); + if (data.items[0] && data.items.length) { + this.set("allSharedWidgets", + data.items.filter(function (widget) { + return widget.WidgetInfo.widget_type != "HEATMAP"; + }).map(function (widget) { + var widgetType = widget.WidgetInfo.widget_type; + var widgetName = widget.WidgetInfo.widget_name; + var widgetId = widget.WidgetInfo.id; + return Em.Object.create({ + id: widgetId, + widgetName: widgetName, + description: widget.WidgetInfo.description, + widgetType: widgetType, + iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png", + serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'), + added: widgetIds.contains(widgetId), + isShared: widget.WidgetInfo.scope == "CLUSTER" + }); + }) + ); + } + this.set('isAllSharedWidgetsLoaded', true); + }, + + allSharedWidgets: [], + mineWidgets: [], + + /** + * load all mine widgets of current user to show on widget browser + * @returns {$.ajax} + */ + loadMineWidgets: function () { + this.set('isMineWidgetsLoaded', false); + return App.ajax.send({ + name: 'widgets.all.mine.get', + sender: this, + data: { + loginName: App.router.get('loginName') + }, + success: 'loadMineWidgetsSuccessCallback' + }); + }, + + /** + * success callback of <code>loadMineWidgets</code> + * @param {object|null} data + */ + loadMineWidgetsSuccessCallback: function (data) { + var widgetIds = this.get('widgets').mapProperty('id'); + if (data.items[0] && data.items.length) { + this.set("mineWidgets", + data.items.filter(function (widget) { + return widget.WidgetInfo.widget_type != "HEATMAP"; + }).map(function (widget) { + var widgetType = widget.WidgetInfo.widget_type; + var widgetName = widget.WidgetInfo.widget_name; + var widgetId = widget.WidgetInfo.id; + return Em.Object.create({ + id: widget.WidgetInfo.id, + widgetName: widgetName, + description: widget.WidgetInfo.description, + widgetType: widgetType, + iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png", + serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'), + added: widgetIds.contains(widgetId), + isShared: widget.WidgetInfo.scope == "CLUSTER" + }); + }) + ); + } else { + this.set("mineWidgets", []); + } + this.set('isMineWidgetsLoaded', true); + }, + + /** + * add widgets, on click handler for "Add" + */ + addWidget: function (event) { + var widgetToAdd = event.context; + var activeLayout = this.get('activeWidgetLayout'); + var widgetIds = activeLayout.get('widgets').map(function(widget) { + return { + "id": widget.get("id") + } + }); + widgetIds.pushObject({ + "id": widgetToAdd.id + }); + var data = { + "WidgetLayoutInfo": { + "display_name": activeLayout.get("displayName"), + "id": activeLayout.get("id"), + "layout_name": activeLayout.get("layoutName"), + "scope": activeLayout.get("scope"), + "section_name": activeLayout.get("sectionName"), + "widgets": widgetIds + } + }; + + widgetToAdd.set('added', !widgetToAdd.added); + return App.ajax.send({ + name: 'widget.layout.edit', + sender: this, + data: { + layoutId: activeLayout.get("id"), + data: data + }, + success: 'updateActiveLayout' + }); + }, + + /** + * hide widgets, on click handler for "Added" + */ + hideWidget: function (event) { + var widgetToHide = event.context; + var activeLayout = this.get('activeWidgetLayout'); + var widgetIds = activeLayout.get('widgets').map(function (widget) { + return { + "id": widget.get("id") + } + }); + var data = { + "WidgetLayoutInfo": { + "display_name": activeLayout.get("displayName"), + "id": activeLayout.get("id"), + "layout_name": activeLayout.get("layoutName"), + "scope": activeLayout.get("scope"), + "section_name": activeLayout.get("sectionName"), + "widgets": widgetIds.filter(function (widget) { + return widget.id !== widgetToHide.id; + }) + } + }; + + widgetToHide.set('added', !widgetToHide.added); + return App.ajax.send({ + name: 'widget.layout.edit', + sender: this, + data: { + layoutId: activeLayout.get("id"), + data: data + }, + success: 'hideWidgetSuccessCallback' + }); + + }, + + /** + * @param {object|null} data + * @param {object} opt + * @param {object} params + */ + hideWidgetSuccessCallback: function (data, opt, params) { + params.data.WidgetLayoutInfo.widgets = params.data.WidgetLayoutInfo.widgets.map(function (widget) { + return { + WidgetInfo: { + id: widget.id + } + } + }); + App.widgetLayoutMapper.map({items: [params.data]}); + this.propertyDidChange('widgets'); + }, + + /** + * update current active widget layout + */ + updateActiveLayout: function () { + this.getActiveWidgetLayout(); + }, + + /** + * delete widgets, on click handler for "Delete" + */ + deleteWidget: function (event) { + var widget = event.context; + var self = this; + var confirmMsg = widget.get('isShared') ? Em.I18n.t('dashboard.widgets.browser.action.delete.shared.bodyMsg').format(widget.widgetName) : Em.I18n.t('dashboard.widgets.browser.action.delete.mine.bodyMsg').format(widget.widgetName); + var bodyMessage = Em.Object.create({ + confirmMsg: confirmMsg, + confirmButton: Em.I18n.t('dashboard.widgets.browser.action.delete.btnMsg') + }); + return App.showConfirmationFeedBackPopup(function (query) { + return App.ajax.send({ + name: 'widget.action.delete', + sender: self, + data: { + id: widget.id + }, + success: 'updateWidgetBrowser' + }); + + }, bodyMessage); + }, + + /** + * update widget browser content after deleted some widget + */ + updateWidgetBrowser: function () { + this.loadAllSharedWidgets(); + this.loadMineWidgets(); + }, + + /** + * Share widgets, on click handler for "Share" + */ + shareWidget: function (event) { + var widget = event.context; + var self = this; + var bodyMessage = Em.Object.create({ + confirmMsg: Em.I18n.t('dashboard.widgets.browser.action.share.confirmation'), + confirmButton: Em.I18n.t('dashboard.widgets.browser.action.share') + }); + return App.showConfirmationFeedBackPopup(function (query) { + return App.ajax.send({ + name: 'widgets.wizard.edit', + sender: self, + data: { + data: { + "WidgetInfo": { + "widget_name": widget.get("widgetName"), + "scope": "CLUSTER" + } + }, + widgetId: widget.get("id") + }, + success: 'updateWidgetBrowser' + }); + }, bodyMessage); + }, + + /** + * create widget + */ + createWidget: function () { + App.router.send('createServiceWidget', Em.Object.create({ + layout: this.get('activeWidgetLayout'), + serviceName: this.get('content.serviceName') + })); + }, + + /** + * edit widget + * @param {App.Widget} content + */ + editWidget: function (content) { + content.set('serviceName', this.get('content.serviceName')); + App.router.send('editServiceWidget', content); + }, + + /** + * launch Widgets Browser popup + * @method showPopup + * @return {App.ModalPopup} + */ + goToWidgetsBrowser: function () { + var self = this; + + return App.ModalPopup.show({ + header: Em.I18n.t('dashboard.widgets.browser.header'), + + classNames: ['common-modal-wrapper', 'widgets-browser-popup'], + modalDialogClasses: ['modal-lg'], + onPrimary: function () { + this.hide(); + self.set('isAllSharedWidgetsLoaded', false); + self.set('allSharedWidgets', []); + self.set('isMineWidgetsLoaded', false); + self.set('mineWidgets', []); + }, + autoHeight: false, + isHideBodyScroll: false, + footerClass: Ember.View.extend({ + templateName: require('templates/common/modal_popups/widget_browser_footer'), + isShowMineOnly: false, + onPrimary: function() { + this.get('parentView').onPrimary(); + } + }), + isShowMineOnly: false, + bodyClass: Ember.View.extend({ + templateName: require('templates/common/modal_popups/widget_browser_popup'), + controller: self, + willInsertElement: function () { + this.get('controller').loadAllSharedWidgets(); + this.get('controller').loadMineWidgets(); + }, + + isLoaded: Em.computed.and('controller.isAllSharedWidgetsLoaded', 'controller.isMineWidgetsLoaded'), + + isWidgetEmptyList: Em.computed.empty('filteredContent'), + + activeService: '', + activeStatus: '', + + content: function () { + if (this.get('parentView.isShowMineOnly')) { + return this.get('controller.mineWidgets'); + } else { + // merge my widgets and all shared widgets, no duplicated is allowed + var content = []; + var widgetMap = {}; + var allWidgets = this.get('controller.allSharedWidgets').concat(this.get('controller.mineWidgets')); + allWidgets.forEach(function(widget) { + if (!widgetMap[widget.get("id")]) { + content.pushObject(widget); + widgetMap[widget.get("id")] = true; + } + }); + return content; + } + }.property('controller.allSharedWidgets.length', 'controller.isAllSharedWidgetsLoaded', + 'controller.mineWidgets.length', 'controller.isMineWidgetsLoaded', 'parentView.isShowMineOnly'), + + /** + * displaying content filtered by service name and status. + */ + filteredContent: function () { + var activeService = this.get('activeService') ? this.get('activeService') : this.get('controller.content.serviceName'); + var result = []; + this.get('content').forEach(function (widget) { + if (widget.get('serviceName').indexOf(activeService) >= 0) { + result.pushObject(widget); + } + }); + return result; + }.property('content', 'activeService', 'activeStatus'), + + /** + * service name filter + */ + services: function () { + var view = this; + var services = App.Service.find().filter(function(item){ + var stackService = App.StackService.find().findProperty('serviceName', item.get('serviceName')); + return stackService.get('isServiceWithWidgets'); + }); + return services.map(function (service) { + return Em.Object.create({ + value: service.get('serviceName'), + label: service.get('displayName'), + isActive: function () { + var activeService = view.get('activeService') ? view.get('activeService') : view.get('controller.content.serviceName'); + return this.get('value') == activeService; + }.property('value', 'view.activeService') + }) + }); + }.property('activeService'), + + filterByService: function (event) { + this.set('activeService', event.context); + }, + + createWidget: function () { + this.get('parentView').onPrimary(); + this.get('controller').createWidget(); + }, + + ensureTooltip: function () { + Em.run.later(this, function () { + App.tooltip($("[rel='shared-icon-tooltip']")); + }, 1000); + }.observes('activeService', 'parentView.isShowMineOnly'), + + didInsertElement: function () { + this.ensureTooltip(); + } + }) + }); + } + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers/main/service/info/summary.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/info/summary.js b/ambari-web/app/controllers/main/service/info/summary.js index d696334..3d7483a 100644 --- a/ambari-web/app/controllers/main/service/info/summary.js +++ b/ambari-web/app/controllers/main/service/info/summary.js @@ -17,7 +17,7 @@ var App = require('app'); -App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMixin, { +App.MainServiceInfoSummaryController = Em.Controller.extend({ name: 'mainServiceInfoSummaryController', selectedFlumeAgent: null, @@ -40,10 +40,6 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix */ isPreviousRangerConfigsCallFailed: false, - layoutNameSuffix: "_dashboard", - - sectionNameSuffix: "_SUMMARY", - /** * HiveServer2 JDBC connection endpoint data * @type {array} @@ -112,18 +108,6 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix ], /** - * Some widget has type `GRAPH` - * - * @type {boolean} - */ - someWidgetGraphExists: Em.computed.someBy('widgets', 'widgetType', 'GRAPH'), - - /** - * @type {boolean} - */ - showTimeRangeControl: Em.computed.or('!isServiceWithEnhancedWidgets', 'someWidgetGraphExists'), - - /** * Set initial Ranger plugins data * @method setRangerPlugins */ @@ -425,437 +409,6 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix }); }, - - /** - * @type {boolean} - */ - isWidgetLayoutsLoaded: false, - - /** - * @type {boolean} - */ - isAllSharedWidgetsLoaded: false, - - /** - * @type {boolean} - */ - isMineWidgetsLoaded: false, - - - /** - * load widget layouts across all users in CLUSTER scope - * @returns {$.ajax} - */ - loadWidgetLayouts: function () { - this.set('isWidgetLayoutsLoaded', false); - return App.ajax.send({ - name: 'widgets.layouts.get', - sender: this, - data: { - sectionName: this.get('sectionName') - }, - success: 'loadWidgetLayoutsSuccessCallback' - }); - }, - - loadWidgetLayoutsSuccessCallback: function (data) { - App.widgetLayoutMapper.map(data); - this.set('isWidgetLayoutsLoaded', true); - }, - - - /** - * load all shared widgets to show on widget browser - * @returns {$.ajax} - */ - loadAllSharedWidgets: function () { - this.set('isAllSharedWidgetsLoaded', false); - return App.ajax.send({ - name: 'widgets.all.shared.get', - sender: this, - success: 'loadAllSharedWidgetsSuccessCallback' - }); - }, - - /** - * success callback of <code>loadAllSharedWidgets</code> - * @param {object|null} data - */ - loadAllSharedWidgetsSuccessCallback: function (data) { - var widgetIds = this.get('widgets').mapProperty('id'); - if (data.items[0] && data.items.length) { - this.set("allSharedWidgets", - data.items.filter(function (widget) { - return widget.WidgetInfo.widget_type != "HEATMAP"; - }).map(function (widget) { - var widgetType = widget.WidgetInfo.widget_type; - var widgetName = widget.WidgetInfo.widget_name; - var widgetId = widget.WidgetInfo.id; - return Em.Object.create({ - id: widgetId, - widgetName: widgetName, - description: widget.WidgetInfo.description, - widgetType: widgetType, - iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png", - serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'), - added: widgetIds.contains(widgetId), - isShared: widget.WidgetInfo.scope == "CLUSTER" - }); - }) - ); - } - this.set('isAllSharedWidgetsLoaded', true); - }, - - allSharedWidgets: [], - mineWidgets: [], - - /** - * load all mine widgets of current user to show on widget browser - * @returns {$.ajax} - */ - loadMineWidgets: function () { - this.set('isMineWidgetsLoaded', false); - return App.ajax.send({ - name: 'widgets.all.mine.get', - sender: this, - data: { - loginName: App.router.get('loginName') - }, - success: 'loadMineWidgetsSuccessCallback' - }); - }, - - /** - * success callback of <code>loadMineWidgets</code> - * @param {object|null} data - */ - loadMineWidgetsSuccessCallback: function (data) { - var widgetIds = this.get('widgets').mapProperty('id'); - if (data.items[0] && data.items.length) { - this.set("mineWidgets", - data.items.filter(function (widget) { - return widget.WidgetInfo.widget_type != "HEATMAP"; - }).map(function (widget) { - var widgetType = widget.WidgetInfo.widget_type; - var widgetName = widget.WidgetInfo.widget_name; - var widgetId = widget.WidgetInfo.id; - return Em.Object.create({ - id: widget.WidgetInfo.id, - widgetName: widgetName, - description: widget.WidgetInfo.description, - widgetType: widgetType, - iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png", - serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'), - added: widgetIds.contains(widgetId), - isShared: widget.WidgetInfo.scope == "CLUSTER" - }); - }) - ); - } else { - this.set("mineWidgets", []); - } - this.set('isMineWidgetsLoaded', true); - }, - - /** - * add widgets, on click handler for "Add" - */ - addWidget: function (event) { - var widgetToAdd = event.context; - var activeLayout = this.get('activeWidgetLayout'); - var widgetIds = activeLayout.get('widgets').map(function(widget) { - return { - "id": widget.get("id") - } - }); - widgetIds.pushObject({ - "id": widgetToAdd.id - }); - var data = { - "WidgetLayoutInfo": { - "display_name": activeLayout.get("displayName"), - "id": activeLayout.get("id"), - "layout_name": activeLayout.get("layoutName"), - "scope": activeLayout.get("scope"), - "section_name": activeLayout.get("sectionName"), - "widgets": widgetIds - } - }; - - widgetToAdd.set('added', !widgetToAdd.added); - return App.ajax.send({ - name: 'widget.layout.edit', - sender: this, - data: { - layoutId: activeLayout.get("id"), - data: data - }, - success: 'updateActiveLayout' - }); - }, - - /** - * hide widgets, on click handler for "Added" - */ - hideWidget: function (event) { - var widgetToHide = event.context; - var activeLayout = this.get('activeWidgetLayout'); - var widgetIds = activeLayout.get('widgets').map(function (widget) { - return { - "id": widget.get("id") - } - }); - var data = { - "WidgetLayoutInfo": { - "display_name": activeLayout.get("displayName"), - "id": activeLayout.get("id"), - "layout_name": activeLayout.get("layoutName"), - "scope": activeLayout.get("scope"), - "section_name": activeLayout.get("sectionName"), - "widgets": widgetIds.filter(function (widget) { - return widget.id !== widgetToHide.id; - }) - } - }; - - widgetToHide.set('added', !widgetToHide.added); - return App.ajax.send({ - name: 'widget.layout.edit', - sender: this, - data: { - layoutId: activeLayout.get("id"), - data: data - }, - success: 'hideWidgetSuccessCallback' - }); - - }, - - /** - * @param {object|null} data - * @param {object} opt - * @param {object} params - */ - hideWidgetSuccessCallback: function (data, opt, params) { - params.data.WidgetLayoutInfo.widgets = params.data.WidgetLayoutInfo.widgets.map(function (widget) { - return { - WidgetInfo: { - id: widget.id - } - } - }); - App.widgetLayoutMapper.map({items: [params.data]}); - this.propertyDidChange('widgets'); - }, - - /** - * update current active widget layout - */ - updateActiveLayout: function () { - this.getActiveWidgetLayout(); - }, - - /** - * delete widgets, on click handler for "Delete" - */ - deleteWidget: function (event) { - var widget = event.context; - var self = this; - var confirmMsg = widget.get('isShared') ? Em.I18n.t('dashboard.widgets.browser.action.delete.shared.bodyMsg').format(widget.widgetName) : Em.I18n.t('dashboard.widgets.browser.action.delete.mine.bodyMsg').format(widget.widgetName); - var bodyMessage = Em.Object.create({ - confirmMsg: confirmMsg, - confirmButton: Em.I18n.t('dashboard.widgets.browser.action.delete.btnMsg') - }); - return App.showConfirmationFeedBackPopup(function (query) { - return App.ajax.send({ - name: 'widget.action.delete', - sender: self, - data: { - id: widget.id - }, - success: 'updateWidgetBrowser' - }); - - }, bodyMessage); - }, - - /** - * update widget browser content after deleted some widget - */ - updateWidgetBrowser: function () { - this.loadAllSharedWidgets(); - this.loadMineWidgets(); - }, - - /** - * Share widgets, on click handler for "Share" - */ - shareWidget: function (event) { - var widget = event.context; - var self = this; - var bodyMessage = Em.Object.create({ - confirmMsg: Em.I18n.t('dashboard.widgets.browser.action.share.confirmation'), - confirmButton: Em.I18n.t('dashboard.widgets.browser.action.share') - }); - return App.showConfirmationFeedBackPopup(function (query) { - return App.ajax.send({ - name: 'widgets.wizard.edit', - sender: self, - data: { - data: { - "WidgetInfo": { - "widget_name": widget.get("widgetName"), - "scope": "CLUSTER" - } - }, - widgetId: widget.get("id") - }, - success: 'updateWidgetBrowser' - }); - }, bodyMessage); - }, - - /** - * create widget - */ - createWidget: function () { - App.router.send('createServiceWidget', Em.Object.create({ - layout: this.get('activeWidgetLayout'), - serviceName: this.get('content.serviceName') - })); - }, - - /** - * edit widget - * @param {App.Widget} content - */ - editWidget: function (content) { - content.set('serviceName', this.get('content.serviceName')); - App.router.send('editServiceWidget', content); - }, - - /** - * launch Widgets Browser popup - * @method showPopup - * @return {App.ModalPopup} - */ - goToWidgetsBrowser: function () { - var self = this; - - return App.ModalPopup.show({ - header: Em.I18n.t('dashboard.widgets.browser.header'), - - classNames: ['common-modal-wrapper', 'widgets-browser-popup'], - modalDialogClasses: ['modal-lg'], - onPrimary: function () { - this.hide(); - self.set('isAllSharedWidgetsLoaded', false); - self.set('allSharedWidgets', []); - self.set('isMineWidgetsLoaded', false); - self.set('mineWidgets', []); - }, - autoHeight: false, - isHideBodyScroll: false, - footerClass: Ember.View.extend({ - templateName: require('templates/common/modal_popups/widget_browser_footer'), - isShowMineOnly: false, - onPrimary: function() { - this.get('parentView').onPrimary(); - } - }), - isShowMineOnly: false, - bodyClass: Ember.View.extend({ - templateName: require('templates/common/modal_popups/widget_browser_popup'), - controller: self, - willInsertElement: function () { - this.get('controller').loadAllSharedWidgets(); - this.get('controller').loadMineWidgets(); - }, - - isLoaded: Em.computed.and('controller.isAllSharedWidgetsLoaded', 'controller.isMineWidgetsLoaded'), - - isWidgetEmptyList: Em.computed.empty('filteredContent'), - - activeService: '', - activeStatus: '', - - content: function () { - if (this.get('parentView.isShowMineOnly')) { - return this.get('controller.mineWidgets'); - } else { - // merge my widgets and all shared widgets, no duplicated is allowed - var content = []; - var widgetMap = {}; - var allWidgets = this.get('controller.allSharedWidgets').concat(this.get('controller.mineWidgets')); - allWidgets.forEach(function(widget) { - if (!widgetMap[widget.get("id")]) { - content.pushObject(widget); - widgetMap[widget.get("id")] = true; - } - }); - return content; - } - }.property('controller.allSharedWidgets.length', 'controller.isAllSharedWidgetsLoaded', - 'controller.mineWidgets.length', 'controller.isMineWidgetsLoaded', 'parentView.isShowMineOnly'), - - /** - * displaying content filtered by service name and status. - */ - filteredContent: function () { - var activeService = this.get('activeService') ? this.get('activeService') : this.get('controller.content.serviceName'); - var result = []; - this.get('content').forEach(function (widget) { - if (widget.get('serviceName').indexOf(activeService) >= 0) { - result.pushObject(widget); - } - }); - return result; - }.property('content', 'activeService', 'activeStatus'), - - /** - * service name filter - */ - services: function () { - var view = this; - var services = App.Service.find().filter(function(item){ - var stackService = App.StackService.find().findProperty('serviceName', item.get('serviceName')); - return stackService.get('isServiceWithWidgets'); - }); - return services.map(function (service) { - return Em.Object.create({ - value: service.get('serviceName'), - label: service.get('displayName'), - isActive: function () { - var activeService = view.get('activeService') ? view.get('activeService') : view.get('controller.content.serviceName'); - return this.get('value') == activeService; - }.property('value', 'view.activeService') - }) - }); - }.property('activeService'), - - filterByService: function (event) { - this.set('activeService', event.context); - }, - - createWidget: function () { - this.get('parentView').onPrimary(); - this.get('controller').createWidget(); - }, - - ensureTooltip: function () { - Em.run.later(this, function () { - App.tooltip($("[rel='shared-icon-tooltip']")); - }, 1000); - }.observes('activeService', 'parentView.isShowMineOnly'), - - didInsertElement: function () { - this.ensureTooltip(); - } - }) - }); - }, - goToView: function(event) { App.router.route(event.context.get('internalAmbariUrl')); } http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js b/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js index e833ead..a46c5e4 100644 --- a/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js +++ b/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js @@ -417,7 +417,7 @@ App.WidgetWizardController = App.WizardController.extend({ var self = this; var successCallBack = function() { self.get('popup').hide(); - App.router.transitionTo('main.services.service.summary', service); + App.router.transitionTo('main.services.service.metrics', service); App.get('router.updateController').updateAll(); }; http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index 7cde3d1..3c4f038 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -2199,6 +2199,7 @@ Em.I18n.translations = { 'services.service.info.menu.summary':'Summary', 'services.service.info.menu.configs':'Configs', 'services.service.info.menu.heatmaps':'Heatmaps', + 'services.service.info.menu.metrics':'Metrics', 'services.service.info.summary.hostsRunningMonitor':'{0}/{1}', 'services.service.info.summary.serversHostCount':'{0} more', http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/styles/enhanced_service_dashboard.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/enhanced_service_dashboard.less b/ambari-web/app/styles/enhanced_service_dashboard.less index 34a4763..00b46a8 100644 --- a/ambari-web/app/styles/enhanced_service_dashboard.less +++ b/ambari-web/app/styles/enhanced_service_dashboard.less @@ -26,6 +26,10 @@ clear: both; + .service-widgets-box { + padding: 10px 1.1% 10px 1.1%; + } + #add-widget-action-box { background-color: @add-widget-btn-color; width: 97%; @@ -69,7 +73,7 @@ width: 93%; } .span2p4 { - width: 22.7%; + width: 24.4%; height: 100%; background-color: white; margin: 5px 0 5px 5px; @@ -188,6 +192,26 @@ } } +@media (min-width: 1200px) { + + .service-metrics-block .service-widgets-box { + padding: 10px 1.3% 10px 1.3%; + } + + #widget_layout .span2p4 { + width: 24.5%; + *width: 24.5%; + } +} + +@media (min-width: 1500px) { + + #widget_layout .span2p4 { + width: 24.6%; + *width: 24.6%; + } +} + #widget-preview { max-width: 200px; margin: auto; http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/templates/main/service/info/metrics.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/service/info/metrics.hbs b/ambari-web/app/templates/main/service/info/metrics.hbs new file mode 100644 index 0000000..6834c06 --- /dev/null +++ b/ambari-web/app/templates/main/service/info/metrics.hbs @@ -0,0 +1,104 @@ +{{! +* 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. +}} + +{{#if view.serviceHasMetrics}} + <div class="service-metrics-block"> + <div class="panel panel-default"> + <div class="panel-heading"> + <div class="row"> + <div class="col-md-7 col-lg-7"> + <h4 class="panel-title">{{t services.service.metrics}}</h4> + </div> + <div class="col-md-5 col-lg-5"> + {{#if showTimeRangeControl}} + {{view view.timeRangeListView}} + {{/if}} + {{#if isServiceWithEnhancedWidgets}} + {{#if isAmbariMetricsInstalled}} + <div class="btn-group pull-right actions"> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + {{t common.actions}} <span class="caret"></span> + </button> + <ul class="dropdown-menu"> + {{#each option in view.widgetActions}} + <li {{bindAttr class="option.layouts:dropdown-submenu"}}> + {{#if option.isAction}} + <a href="#" {{action doWidgetAction option.action target="view"}}> + <i {{bindAttr class="option.class"}}></i> + {{option.label}} + </a> + {{#if option.layouts}} + <ul class="dropdown-menu"> + {{#each layout in option.layouts}} + <li> + <a href="javascript:void(0);"> + {{layout.layoutName}} + </a> + </li> + {{/each}} + </ul> + {{/if}} + {{/if}} + </li> + {{/each}} + </ul> + </div> + {{/if}} + {{/if}} + </div> + </div> + </div> + <div class="panel-body service-widgets-box"> + {{#if isServiceWithEnhancedWidgets}} + <div id="widget_layout" class="thumbnails"> + {{#each widget in controller.widgets}} + <div class="widget span2p4" {{bindAttr id="widget.id"}}> + {{view widget.viewClass contentBinding="widget" idBinding="widget.id"}} + </div> + {{/each}} + {{#if isAmbariMetricsInstalled}} + <div class="span2p4"> + <button id="add-widget-action-box" {{action "goToWidgetsBrowser" controller.content + target="controller"}} + rel="add-widget-tooltip" {{translateAttr + data-original-title="dashboard.widgets.addButton.tooltip"}}> + <i class="glyphicon glyphicon-plus"></i></button> + </div> + {{/if}} + </div> + {{/if}} + <table class="graphs"> + {{#each graphs in view.serviceMetricGraphs}} + <tr> + {{#each graph in graphs}} + <td> + <div> + {{view graph}} + </div> + </td> + {{/each}} + </tr> + {{/each}} + </table> + </div> + </div> + </div> +{{/if}} + + + http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/templates/main/service/info/summary.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/service/info/summary.hbs b/ambari-web/app/templates/main/service/info/summary.hbs index 075cae0..b0c9e7f 100644 --- a/ambari-web/app/templates/main/service/info/summary.hbs +++ b/ambari-web/app/templates/main/service/info/summary.hbs @@ -134,90 +134,6 @@ </div> </div> </div> - {{! widgets in the metrics panel are loaded seperately from summary page text information - and does not get block due to any global API poller information }} - {{#if view.isServiceMetricLoaded}} - <div class="service-metrics-block"> - <div class="panel panel-default"> - <div class="panel-heading"> - <div class="row"> - <div class="col-md-7 col-lg-7"> - <h4 class="panel-title">{{t services.service.metrics}}</h4> - </div> - <div class="col-md-5 col-lg-5"> - {{#if showTimeRangeControl}} - {{view view.timeRangeListView}} - {{/if}} - {{#if isServiceWithEnhancedWidgets}} - {{#if isAmbariMetricsInstalled}} - <div class="btn-group pull-right actions"> - <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> - {{t common.actions}} <span class="caret"></span> - </button> - <ul class="dropdown-menu"> - {{#each option in view.widgetActions}} - <li {{bindAttr class="option.layouts:dropdown-submenu"}}> - {{#if option.isAction}} - <a href="#" {{action doWidgetAction option.action target="view"}}> - <i {{bindAttr class="option.class"}}></i> - {{option.label}} - </a> - {{#if option.layouts}} - <ul class="dropdown-menu"> - {{#each layout in option.layouts}} - <li> - <a href="javascript:void(0);"> - {{layout.layoutName}} - </a> - </li> - {{/each}} - </ul> - {{/if}} - {{/if}} - </li> - {{/each}} - </ul> - </div> - {{/if}} - {{/if}} - </div> - </div> - </div> - <div class="panel-body"> - {{#if isServiceWithEnhancedWidgets}} - <div id="widget_layout" class="thumbnails"> - {{#each widget in controller.widgets}} - <div class="widget span2p4" {{bindAttr id="widget.id"}}> - {{view widget.viewClass contentBinding="widget" idBinding="widget.id"}} - </div> - {{/each}} - {{#if isAmbariMetricsInstalled}} - <div class="span2p4"> - <button id="add-widget-action-box" - {{action "goToWidgetsBrowser" controller.content target="controller"}} - rel="add-widget-tooltip" {{translateAttr data-original-title="dashboard.widgets.addButton.tooltip"}}> - <i class="glyphicon glyphicon-plus"></i></button> - </div> - {{/if}} - </div> - {{/if}} - <table class="graphs"> - {{#each graphs in view.serviceMetricGraphs}} - <tr> - {{#each graph in graphs}} - <td> - <div> - {{view graph}} - </div> - </td> - {{/each}} - </tr> - {{/each}} - </table> - </div> - </div> - </div> - {{/if}} </div> {{#if view.collapsedSections}} http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/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 e942eb1..df26a9d 100644 --- a/ambari-web/app/templates/main/service/item.hbs +++ b/ambari-web/app/templates/main/service/item.hbs @@ -16,7 +16,10 @@ * limitations under the License. }} -{{view App.MainServiceInfoMenuView configTabBinding="view.hasConfigTab" heatmapTabBinding="view.hasHeatmapTab"}} +{{view App.MainServiceInfoMenuView + configTabBinding="view.hasConfigTab" + heatmapTabBinding="view.hasHeatmapTab" + metricTabBinding="view.hasMetricTab"}} {{#isAuthorized "SERVICE.RUN_CUSTOM_COMMAND, SERVICE.RUN_SERVICE_CHECK, SERVICE.START_STOP, SERVICE.TOGGLE_MAINTENANCE, SERVICE.ENABLE_HA"}} <div class="service-button"> http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js index 8031434..50729a7 100644 --- a/ambari-web/app/views.js +++ b/ambari-web/app/views.js @@ -348,6 +348,7 @@ require('views/main/charts/heatmap/heatmap_rack'); require('views/main/charts/heatmap/heatmap_host'); require('views/main/charts/heatmap/heatmap_host_detail'); require('views/main/service/info/heatmap_view'); +require('views/main/service/info/metrics_view'); require('views/main/service/widgets/create/wizard_view'); require('views/main/service/widgets/create/step1_view'); http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/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..89d5401 100644 --- a/ambari-web/app/views/main/service/info/menu.js +++ b/ambari-web/app/views/main/service/info/menu.js @@ -45,6 +45,13 @@ App.MainServiceInfoMenuView = Em.CollectionView.extend({ routing: 'configs' }); } + if (this.get('metricTab')) { + menuItems.push({ + label: Em.I18n.t('services.service.info.menu.metrics'), + id: 'metrics-service-tab', + routing: 'metrics' + }); + } return menuItems; }.property(), http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views/main/service/info/metrics_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/info/metrics_view.js b/ambari-web/app/views/main/service/info/metrics_view.js new file mode 100644 index 0000000..161dce1 --- /dev/null +++ b/ambari-web/app/views/main/service/info/metrics_view.js @@ -0,0 +1,290 @@ +/** + * 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'); +var misc = require('utils/misc'); +require('views/main/service/service'); +require('data/service_graph_config'); + +App.MainServiceInfoMetricsView = Em.View.extend(App.Persist, App.TimeRangeMixin, { + templateName: require('templates/main/service/info/metrics'), + /** + * @property {Number} chunkSize - number of columns in Metrics section + */ + chunkSize: 5, + + service: null, + + svc: function () { + var svc = this.get('controller.content'); + var svcName = svc.get('serviceName'); + if (svcName) { + switch (svcName.toLowerCase()) { + case 'hdfs': + svc = App.HDFSService.find().objectAt(0); + break; + case 'yarn': + svc = App.YARNService.find().objectAt(0); + break; + case 'hbase': + svc = App.HBaseService.find().objectAt(0); + break; + case 'flume': + svc = App.FlumeService.find().objectAt(0); + break; + default: + break; + } + } + return svc; + }.property('controller.content.serviceName').volatile(), + + getServiceModel: function (serviceName) { + var extended = App.Service.extendedModel[serviceName]; + if (extended) { + return App[extended].find().objectAt(0); + } + return App.Service.find(serviceName); + }, + + serviceName: Em.computed.alias('service.serviceName'), + + /** + * Contains graphs for this particular service + */ + serviceMetricGraphs: [], + + /** + * @type {boolean} + * @default false + */ + serviceHasMetrics: false, + + /** + * Key-name to store time range in Persist + * @type {string} + */ + persistKey: Em.computed.format('time-range-service-{0}', 'service.serviceName'), + + didInsertElement: function () { + var svcName = this.get('controller.content.serviceName'); + this.set('service', this.getServiceModel(svcName)); + var isMetricsSupported = svcName !== 'STORM' || App.get('isStormMetricsSupported'); + + this.get('controller').getActiveWidgetLayout(); + if (App.get('supports.customizedWidgetLayout')) { + this.get('controller').loadWidgetLayouts(); + } + + if (svcName && isMetricsSupported) { + var allServices = require('data/service_graph_config'); + this.constructGraphObjects(allServices[svcName.toLowerCase()]); + } + this.makeSortable(); + this.addWidgetTooltip(); + }, + + addWidgetTooltip: function() { + Em.run.later(this, function () { + App.tooltip($("[rel='add-widget-tooltip']")); + // enable description show up on hover + $('.img-thumbnail').hoverIntent(function() { + if ($(this).is('hover')) { + $(this).find('.hidden-description').delay(1000).fadeIn(200).end(); + } + }, function() { + $(this).find('.hidden-description').stop().hide().end(); + }); + }, 1000); + }, + + willDestroyElement: function() { + $("[rel='add-widget-tooltip']").tooltip('destroy'); + $('.img-thumbnail').off(); + $('#widget_layout').sortable('destroy'); + $('.widget.span2p4').detach().remove(); + this.get('serviceMetricGraphs').clear(); + this.set('service', null); + }, + + /* + * Find the graph class associated with the graph name, and split + * the array into sections of 5 for displaying on the page + * (will only display rows with 5 items) + */ + constructGraphObjects: function (graphNames) { + var self = this, + stackService = App.StackService.find(this.get('controller.content.serviceName')); + + if (!graphNames && !stackService.get('isServiceWithWidgets')) { + this.get('serviceMetricGraphs').clear(); + this.set('serviceHasMetrics', false); + return; + } + + // load time range(currentTimeRangeIndex) for current service from server + this.getUserPref(self.get('persistKey')).complete(function () { + var result = [], graphObjects = [], chunkSize = self.get('chunkSize'); + if (graphNames) { + graphNames.forEach(function (graphName) { + graphObjects.push(App["ChartServiceMetrics" + graphName].extend()); + }); + } + while (graphObjects.length) { + result.push(graphObjects.splice(0, chunkSize)); + } + self.set('serviceMetricGraphs', result); + self.set('serviceHasMetrics', true); + }); + }, + + getUserPrefSuccessCallback: function (response, request) { + if (response) { + this.set('currentTimeRangeIndex', response); + } + }, + + getUserPrefErrorCallback: function (request) { + if (request.status === 404) { + this.postUserPref(this.get('persistKey'), 0); + this.set('currentTimeRangeIndex', 0); + } + }, + + /** + * list of static actions of widget + * @type {Array} + */ + staticGeneralWidgetActions: [ + Em.Object.create({ + label: Em.I18n.t('dashboard.widgets.actions.browse'), + class: 'glyphicon glyphicon-th', + action: 'goToWidgetsBrowser', + isAction: true + }) + ], + + /** + *list of static actions of widget accessible to Admin/Operator privelege + * @type {Array} + */ + + staticAdminPrivelegeWidgetActions: [ + Em.Object.create({ + label: Em.I18n.t('dashboard.widgets.create'), + class: 'glyphicon glyphicon-plus', + action: 'createWidget', + isAction: true + }) + ], + + /** + * List of static actions related to widget layout + */ + staticWidgetLayoutActions: [ + Em.Object.create({ + label: Em.I18n.t('dashboard.widgets.layout.save'), + class: 'glyphicon glyphicon-download-alt', + action: 'saveLayout', + isAction: true + }), + Em.Object.create({ + label: Em.I18n.t('dashboard.widgets.layout.import'), + class: 'glyphicon glyphicon-file', + isAction: true, + layouts: App.WidgetLayout.find() + }) + ], + + /** + * @type {Array} + */ + widgetActions: function() { + var options = []; + if (App.isAuthorized('SERVICE.MODIFY_CONFIGS')) { + if (App.supports.customizedWidgetLayout) { + options.pushObjects(this.get('staticWidgetLayoutActions')); + } + options.pushObjects(this.get('staticAdminPrivelegeWidgetActions')); + } + options.pushObjects(this.get('staticGeneralWidgetActions')); + return options; + }.property(''), + + /** + * call action function defined in controller + * @param event + */ + doWidgetAction: function(event) { + if($.isFunction(this.get('controller')[event.context])) { + this.get('controller')[event.context].apply(this.get('controller')); + } + }, + + /** + * onclick handler for a time range option + * @param {object} event + */ + setTimeRange: function (event) { + var graphs = this.get('controller.widgets').filterProperty('widgetType', 'GRAPH'), + callback = function () { + graphs.forEach(function (widget) { + widget.set('properties.time_range', event.context.value); + }); + }; + this._super(event, callback); + + // Preset time range is specified by user + if (event.context.value !== '0') { + callback(); + } + }, + + /** + * Define if some widget is currently moving + * @type {boolean} + */ + isMoving: false, + + /** + * Make widgets' list sortable on New Dashboard style + */ + makeSortable: function () { + var self = this; + $('html').on('DOMNodeInserted', '#widget_layout', function () { + $(this).sortable({ + items: "> div", + cursor: "move", + tolerance: "pointer", + scroll: false, + update: function () { + var widgets = misc.sortByOrder($("#widget_layout .widget").map(function () { + return this.id; + }), self.get('controller.widgets')); + self.get('controller').saveWidgetLayout(widgets); + }, + activate: function () { + self.set('isMoving', true); + }, + deactivate: function () { + self.set('isMoving', false); + } + }).disableSelection(); + $('html').off('DOMNodeInserted', '#widget_layout'); + }); + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views/main/service/info/summary.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/info/summary.js b/ambari-web/app/views/main/service/info/summary.js index 91b2ca3..a4769e6 100644 --- a/ambari-web/app/views/main/service/info/summary.js +++ b/ambari-web/app/views/main/service/info/summary.js @@ -21,13 +21,10 @@ var misc = require('utils/misc'); require('views/main/service/service'); require('data/service_graph_config'); -App.MainServiceInfoSummaryView = Em.View.extend(App.Persist, App.TimeRangeMixin, { +App.MainServiceInfoSummaryView = Em.View.extend({ templateName: require('templates/main/service/info/summary'), - /** - * @property {Number} chunkSize - number of columns in Metrics section - */ - chunkSize: 5, - attributes:null, + + attributes: null, /** * Contain array with list of master components from <code>App.Service.hostComponets</code> which are @@ -165,6 +162,47 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.Persist, App.TimeRangeMixin, Em.run.once(self, 'setComponentsContent'); }.observes('service.hostComponents.length', '[email protected]', '[email protected]'), + loadServiceSummary: function () { + var serviceName = this.get('serviceName'); + var serviceSummaryView = null; + + if (!serviceName) { + return; + } + + if (this.get('oldServiceName')) { + // do not delete it! + return; + } + + var customServiceView = this.get('serviceCustomViewsMap')[serviceName]; + if (customServiceView) { + serviceSummaryView = customServiceView.extend({ + service: this.get('service') + }); + } else { + serviceSummaryView = Em.View.extend(App.MainDashboardServiceViewWrapper, { + templateName: this.get('templatePathPrefix') + 'base' + }); + } + this.set('serviceSummaryView', serviceSummaryView); + this.set('oldServiceName', serviceName); + }.observes('serviceName'), + + didInsertElement: function () { + this._super(); + var svcName = this.get('controller.content.serviceName'); + this.set('service', this.getServiceModel(svcName)); + App.loadTimer.finish('Service Summary Page'); + }, + + willDestroyElement: function() { + this.set('service', null); + this.get('mastersObj').clear(); + this.get('slavesObj').clear(); + this.get('clientObj').clear(); + }, + setComponentsContent: function() { Em.run.next(function() { if (Em.isNone(this.get('service'))) { @@ -372,270 +410,5 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.Persist, App.TimeRangeMixin, rollingRestartStaleConfigSlaveComponents: function (componentName) { batchUtils.launchHostComponentRollingRestart(componentName.context, this.get('service.displayName'), this.get('service.passiveState') === "ON", true); - }, - - /* - * Find the graph class associated with the graph name, and split - * the array into sections of 5 for displaying on the page - * (will only display rows with 5 items) - */ - constructGraphObjects: function (graphNames) { - var self = this, - stackService = App.StackService.find(this.get('controller.content.serviceName')); - - if (!graphNames && !stackService.get('isServiceWithWidgets')) { - this.get('serviceMetricGraphs').clear(); - this.set('isServiceMetricLoaded', false); - return; - } - - // load time range(currentTimeRangeIndex) for current service from server - this.getUserPref(self.get('persistKey')).complete(function () { - var result = [], graphObjects = [], chunkSize = self.get('chunkSize'); - if (graphNames) { - graphNames.forEach(function (graphName) { - graphObjects.push(App["ChartServiceMetrics" + graphName].extend()); - }); - } - while (graphObjects.length) { - result.push(graphObjects.splice(0, chunkSize)); - } - self.set('serviceMetricGraphs', result); - self.set('isServiceMetricLoaded', true); - }); - }, - - /** - * Contains graphs for this particular service - */ - serviceMetricGraphs: [], - - /** - * @type {boolean} - * @default false - */ - isServiceMetricLoaded: false, - - /** - * Key-name to store time range in Persist - * @type {string} - */ - persistKey: Em.computed.format('time-range-service-{0}', 'service.serviceName'), - - getUserPrefSuccessCallback: function (response, request) { - if (response) { - this.set('currentTimeRangeIndex', response); - } - }, - - getUserPrefErrorCallback: function (request) { - if (request.status === 404) { - this.postUserPref(this.get('persistKey'), 0); - this.set('currentTimeRangeIndex', 0); - } - }, - - /** - * list of static actions of widget - * @type {Array} - */ - staticGeneralWidgetActions: [ - Em.Object.create({ - label: Em.I18n.t('dashboard.widgets.actions.browse'), - class: 'glyphicon glyphicon-th', - action: 'goToWidgetsBrowser', - isAction: true - }) - ], - - /** - *list of static actions of widget accessible to Admin/Operator privelege - * @type {Array} - */ - - staticAdminPrivelegeWidgetActions: [ - Em.Object.create({ - label: Em.I18n.t('dashboard.widgets.create'), - class: 'glyphicon glyphicon-plus', - action: 'createWidget', - isAction: true - }) - ], - - /** - * List of static actions related to widget layout - */ - staticWidgetLayoutActions: [ - Em.Object.create({ - label: Em.I18n.t('dashboard.widgets.layout.save'), - class: 'glyphicon glyphicon-download-alt', - action: 'saveLayout', - isAction: true - }), - Em.Object.create({ - label: Em.I18n.t('dashboard.widgets.layout.import'), - class: 'glyphicon glyphicon-file', - isAction: true, - layouts: App.WidgetLayout.find() - }) - ], - - /** - * @type {Array} - */ - widgetActions: function() { - var options = []; - if (App.isAuthorized('SERVICE.MODIFY_CONFIGS')) { - if (App.supports.customizedWidgetLayout) { - options.pushObjects(this.get('staticWidgetLayoutActions')); - } - options.pushObjects(this.get('staticAdminPrivelegeWidgetActions')); - } - options.pushObjects(this.get('staticGeneralWidgetActions')); - return options; - }.property(''), - - /** - * call action function defined in controller - * @param event - */ - doWidgetAction: function(event) { - if($.isFunction(this.get('controller')[event.context])) { - this.get('controller')[event.context].apply(this.get('controller')); - } - }, - - /** - * onclick handler for a time range option - * @param {object} event - */ - setTimeRange: function (event) { - var graphs = this.get('controller.widgets').filterProperty('widgetType', 'GRAPH'), - callback = function () { - graphs.forEach(function (widget) { - widget.set('properties.time_range', event.context.value); - }); - }; - this._super(event, callback); - - // Preset time range is specified by user - if (event.context.value !== '0') { - callback(); - } - }, - - loadServiceSummary: function () { - var serviceName = this.get('serviceName'); - var serviceSummaryView = null; - - if (!serviceName) { - return; - } - - if (this.get('oldServiceName')) { - // do not delete it! - return; - } - - var customServiceView = this.get('serviceCustomViewsMap')[serviceName]; - if (customServiceView) { - serviceSummaryView = customServiceView.extend({ - service: this.get('service') - }); - } else { - serviceSummaryView = Em.View.extend(App.MainDashboardServiceViewWrapper, { - templateName: this.get('templatePathPrefix') + 'base' - }); - } - this.set('serviceSummaryView', serviceSummaryView); - this.set('oldServiceName', serviceName); - }.observes('serviceName'), - - - /** - * Service metrics panel not displayed when metrics service (ex:Ganglia) is not in stack definition. - * - * @type {boolean} - */ - isNoServiceMetricsService: Em.computed.equal('App.services.serviceMetrics.length', 0), - - didInsertElement: function () { - this._super(); - var svcName = this.get('controller.content.serviceName'); - this.set('service', this.getServiceModel(svcName)); - var isMetricsSupported = svcName !== 'STORM' || App.get('isStormMetricsSupported'); - - this.get('controller').getActiveWidgetLayout(); - if (App.get('supports.customizedWidgetLayout')) { - this.get('controller').loadWidgetLayouts(); - } - - if (svcName && isMetricsSupported) { - var allServices = require('data/service_graph_config'); - this.constructGraphObjects(allServices[svcName.toLowerCase()]); - } - this.makeSortable(); - this.addWidgetTooltip(); - App.loadTimer.finish('Service Summary Page'); - }, - - addWidgetTooltip: function() { - Em.run.later(this, function () { - App.tooltip($("[rel='add-widget-tooltip']")); - // enable description show up on hover - $('.img-thumbnail').hoverIntent(function() { - if ($(this).is('hover')) { - $(this).find('.hidden-description').delay(1000).fadeIn(200).end(); - } - }, function() { - $(this).find('.hidden-description').stop().hide().end(); - }); - }, 1000); - }, - - willDestroyElement: function() { - $("[rel='add-widget-tooltip']").tooltip('destroy'); - $('.img-thumbnail').off(); - $('#widget_layout').sortable('destroy'); - $('.widget.span2p4').detach().remove(); - this.get('serviceMetricGraphs').clear(); - this.set('service', null); - this.get('mastersObj').clear(); - this.get('slavesObj').clear(); - this.get('clientObj').clear(); - }, - - /** - * Define if some widget is currently moving - * @type {boolean} - */ - isMoving: false, - - /** - * Make widgets' list sortable on New Dashboard style - */ - makeSortable: function () { - var self = this; - $('html').on('DOMNodeInserted', '#widget_layout', function () { - $(this).sortable({ - items: "> div", - cursor: "move", - tolerance: "pointer", - scroll: false, - update: function () { - var widgets = misc.sortByOrder($("#widget_layout .widget").map(function () { - return this.id; - }), self.get('controller.widgets')); - self.get('controller').saveWidgetLayout(widgets); - }, - activate: function () { - self.set('isMoving', true); - }, - deactivate: function () { - self.set('isMoving', false); - } - }).disableSelection(); - $('html').off('DOMNodeInserted', '#widget_layout'); - }); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views/main/service/item.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/item.js b/ambari-web/app/views/main/service/item.js index 43d75e6..45c783b 100644 --- a/ambari-web/app/views/main/service/item.js +++ b/ambari-web/app/views/main/service/item.js @@ -326,6 +326,12 @@ App.MainServiceItemView = Em.View.extend({ return App.get('services.servicesWithHeatmapTab').contains(this.get('controller.content.serviceName')); }.property('controller.content.serviceName', 'App.services.servicesWithHeatmapTab'), + hasMetricTab: function() { + let serviceName = this.get('controller.content.serviceName'); + let graphs = require('data/service_graph_config')[serviceName.toLowerCase()]; + return graphs || App.StackService.find(serviceName).get('isServiceWithWidgets'); + }.property('controller.content.serviceName'), + didInsertElement: function () { this.get('controller').setStartStopState(); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/test/controllers/main/service/info/metric_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/service/info/metric_test.js b/ambari-web/test/controllers/main/service/info/metric_test.js new file mode 100644 index 0000000..5ef6279 --- /dev/null +++ b/ambari-web/test/controllers/main/service/info/metric_test.js @@ -0,0 +1,110 @@ +/** + * 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'); +require('controllers/main/service/info/metric'); +var testHelpers = require('test/helpers'); +function getController() { + return App.MainServiceInfoMetricsController.create(); +} + +describe('App.MainServiceInfoMetricsController', function () { + + var controller; + + beforeEach(function () { + controller = App.MainServiceInfoMetricsController.create(); + }); + + App.TestAliases.testAsComputedOr(getController(), 'showTimeRangeControl', ['!isServiceWithEnhancedWidgets', 'someWidgetGraphExists']); + + + describe("#getActiveWidgetLayout() for Enhanced Dashboard", function () { + + it("make GET call", function () { + controller.reopen({ + isServiceWithEnhancedWidgets: true, + content: Em.Object.create({serviceName: 'HDFS'}) + }); + controller.getActiveWidgetLayout(); + expect(testHelpers.findAjaxRequest('name', 'widgets.layouts.active.get')).to.exists; + }); + }); + + describe("#getActiveWidgetLayoutSuccessCallback()", function () { + beforeEach(function () { + sinon.stub( App.widgetLayoutMapper, 'map'); + sinon.stub( App.widgetMapper, 'map'); + }); + afterEach(function () { + App.widgetLayoutMapper.map.restore(); + App.widgetMapper.map.restore(); + }); + it("isWidgetLayoutsLoaded should be set to true", function () { + controller.reopen({ + isServiceWithEnhancedWidgets: true, + content: Em.Object.create({serviceName: 'HDFS'}) + }); + controller.getActiveWidgetLayoutSuccessCallback({items:[{ + WidgetLayoutInfo: {} + }]}); + expect(controller.get('isWidgetsLoaded')).to.be.true; + }); + + }); + + describe("#hideWidgetSuccessCallback()", function () { + beforeEach(function () { + sinon.stub(App.widgetLayoutMapper, 'map'); + sinon.stub(controller, 'propertyDidChange'); + var params = { + data: { + WidgetLayoutInfo: { + widgets: [ + {id: 1} + ] + } + } + }; + controller.hideWidgetSuccessCallback({}, {}, params); + }); + afterEach(function () { + App.widgetLayoutMapper.map.restore(); + controller.propertyDidChange.restore(); + }); + it("mapper is called with valid data", function () { + expect(App.widgetLayoutMapper.map.calledWith({ + items: [{ + WidgetLayoutInfo: { + widgets: [ + { + WidgetInfo: { + id: 1 + } + } + ] + } + }] + })).to.be.true; + }); + it('`widgets` is forced to be recalculated', function () { + expect(controller.propertyDidChange.calledWith('widgets')).to.be.true; + }); + }); + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/test/controllers/main/service/info/summary_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/service/info/summary_test.js b/ambari-web/test/controllers/main/service/info/summary_test.js index 51dd595..e5cc32a 100644 --- a/ambari-web/test/controllers/main/service/info/summary_test.js +++ b/ambari-web/test/controllers/main/service/info/summary_test.js @@ -18,7 +18,6 @@ var App = require('app'); require('controllers/main/service/info/summary'); -var testHelpers = require('test/helpers'); function getController() { return App.MainServiceInfoSummaryController.create(); } @@ -31,8 +30,6 @@ describe('App.MainServiceInfoSummaryController', function () { controller = App.MainServiceInfoSummaryController.create(); }); -App.TestAliases.testAsComputedOr(getController(), 'showTimeRangeControl', ['!isServiceWithEnhancedWidgets', 'someWidgetGraphExists']); - describe('#setRangerPlugins', function () { var cases = [ @@ -184,77 +181,4 @@ App.TestAliases.testAsComputedOr(getController(), 'showTimeRangeControl', ['!isS }); - describe("#getActiveWidgetLayout() for Enhanced Dashboard", function () { - - it("make GET call", function () { - var _controller = App.MainServiceInfoSummaryController.create({ - isServiceWithEnhancedWidgets: true, - content: Em.Object.create({serviceName: 'HDFS'}) - }); - _controller.getActiveWidgetLayout(); - expect(testHelpers.findAjaxRequest('name', 'widgets.layouts.active.get')).to.exists; - }); - }); - - describe("#getActiveWidgetLayoutSuccessCallback()", function () { - beforeEach(function () { - sinon.stub( App.widgetLayoutMapper, 'map'); - sinon.stub( App.widgetMapper, 'map'); - }); - afterEach(function () { - App.widgetLayoutMapper.map.restore(); - App.widgetMapper.map.restore(); - }); - it("isWidgetLayoutsLoaded should be set to true", function () { - var _controller = App.MainServiceInfoSummaryController.create({ - isServiceWithEnhancedWidgets: true, - content: Em.Object.create({serviceName: 'HDFS'}) - }); - _controller.getActiveWidgetLayoutSuccessCallback({items:[{ - WidgetLayoutInfo: {} - }]}); - expect(_controller.get('isWidgetsLoaded')).to.be.true; - }); - - }); - - describe("#hideWidgetSuccessCallback()", function () { - beforeEach(function () { - sinon.stub(App.widgetLayoutMapper, 'map'); - sinon.stub(controller, 'propertyDidChange'); - var params = { - data: { - WidgetLayoutInfo: { - widgets: [ - {id: 1} - ] - } - } - }; - controller.hideWidgetSuccessCallback({}, {}, params); - }); - afterEach(function () { - App.widgetLayoutMapper.map.restore(); - controller.propertyDidChange.restore(); - }); - it("mapper is called with valid data", function () { - expect(App.widgetLayoutMapper.map.calledWith({ - items: [{ - WidgetLayoutInfo: { - widgets: [ - { - WidgetInfo: { - id: 1 - } - } - ] - } - }] - })).to.be.true; - }); - it('`widgets` is forced to be recalculated', function () { - expect(controller.propertyDidChange.calledWith('widgets')).to.be.true; - }); - }); - }); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/test/views/main/service/info/metrics_view_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/main/service/info/metrics_view_test.js b/ambari-web/test/views/main/service/info/metrics_view_test.js new file mode 100644 index 0000000..916d451 --- /dev/null +++ b/ambari-web/test/views/main/service/info/metrics_view_test.js @@ -0,0 +1,334 @@ +/** + * 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'); +require('views/main/service/info/metrics_view'); + +describe('App.MainServiceInfoMetricsView', function() { + + var view = App.MainServiceInfoMetricsView.create({ + controller: Em.Object.create({ + content: Em.Object.create({ + id: 'HDFS', + serviceName: 'HDFS', + hostComponents: [] + }), + getActiveWidgetLayout: Em.K, + loadWidgetLayouts: Em.K + }), + service: Em.Object.create() + }); + + describe("#getServiceModel()", function() { + + beforeEach(function() { + sinon.stub(App.Service, 'find').returns({serviceName: 'S1'}); + sinon.stub(App.HDFSService, 'find').returns([{serviceName: 'HDFS'}]); + }); + afterEach(function() { + App.Service.find.restore(); + App.HDFSService.find.restore(); + }); + + it("HDFS service", function() { + expect(view.getServiceModel('HDFS')).to.eql({serviceName: 'HDFS'}); + }); + + it("Simple model service", function() { + expect(view.getServiceModel('S1')).to.eql({serviceName: 'S1'}); + }); + }); + + describe("#constructGraphObjects()", function() { + var mock = Em.Object.create({ + isServiceWithWidgets: false + }); + + beforeEach(function() { + sinon.stub(App.StackService, 'find').returns(mock); + sinon.stub(view, 'getUserPref').returns({ + complete: function(callback){callback();} + }) + }); + afterEach(function() { + App.StackService.find.restore(); + view.getUserPref.restore(); + }); + + it("metrics not loaded", function() { + mock.set('isServiceWithWidgets', false); + view.constructGraphObjects(null); + expect(view.get('serviceHasMetrics')).to.be.false; + expect(view.getUserPref.called).to.be.false; + }); + + it("metrics loaded", function() { + App.ChartServiceMetricsG1 = Em.Object.extend(); + mock.set('isServiceWithWidgets', true); + view.constructGraphObjects(['G1']); + expect(view.get('serviceHasMetrics')).to.be.true; + expect(view.getUserPref.calledOnce).to.be.true; + expect(view.get('serviceMetricGraphs')).to.not.be.empty; + }); + }); + + describe("#getUserPrefSuccessCallback()", function() { + + it("currentTimeRangeIndex should be set", function() { + view.getUserPrefSuccessCallback(1); + expect(view.get('currentTimeRangeIndex')).to.equal(1); + }); + }); + + describe("#getUserPrefErrorCallback()", function() { + + beforeEach(function() { + sinon.stub(view, 'postUserPref'); + }); + afterEach(function() { + view.postUserPref.restore(); + }); + + it("request.status = 404", function() { + view.getUserPrefErrorCallback({status: 404}); + expect(view.get('currentTimeRangeIndex')).to.equal(0); + expect(view.postUserPref.calledOnce).to.be.true; + }); + + it("request.status = 403", function() { + view.getUserPrefErrorCallback({status: 403}); + expect(view.postUserPref.called).to.be.false; + }); + }); + + describe("#widgetActions", function() { + + beforeEach(function() { + this.mock = sinon.stub(App, 'isAuthorized'); + view.setProperties({ + staticWidgetLayoutActions: [{id: 1}], + staticAdminPrivelegeWidgetActions: [{id: 2}], + staticGeneralWidgetActions: [{id: 3}] + }); + }); + afterEach(function() { + this.mock.restore(); + }); + + it("not authorized", function() { + this.mock.returns(false); + view.propertyDidChange('widgetActions'); + expect(view.get('widgetActions').mapProperty('id')).to.eql([3]); + }); + + it("is authorized", function() { + this.mock.returns(true); + App.supports.customizedWidgetLayout = true; + view.propertyDidChange('widgetActions'); + expect(view.get('widgetActions').mapProperty('id')).to.eql([1, 2, 3]); + }); + }); + + describe("#doWidgetAction()", function() { + + beforeEach(function() { + view.set('controller.action1', Em.K); + sinon.stub(view.get('controller'), 'action1'); + }); + afterEach(function() { + view.get('controller').action1.restore(); + }); + + it("action exist", function() { + view.doWidgetAction({context: 'action1'}); + expect(view.get('controller').action1.calledOnce).to.be.true; + }); + }); + + describe("#setTimeRange", function() { + + it("range = 0", function() { + var widget = Em.Object.create({ + widgetType: 'GRAPH', + properties: { + time_range: '0' + } + }); + view.set('controller.widgets', [widget]); + view.setTimeRange({context: {value: '0'}}); + expect(widget.get('properties').time_range).to.be.equal('0') + }); + + it("range = 1", function() { + var widget = Em.Object.create({ + widgetType: 'GRAPH', + properties: { + time_range: 0 + } + }); + view.set('controller.widgets', [widget]); + view.setTimeRange({context: {value: '1'}}); + expect(widget.get('properties').time_range).to.be.equal('1') + }); + }); + + describe("#makeSortable()", function() { + var mock = { + on: function(arg1, arg2, callback) { + callback(); + }, + off: Em.K, + sortable: function() { + return { + disableSelection: Em.K + } + } + }; + + beforeEach(function() { + sinon.stub(window, '$').returns(mock); + sinon.spy(mock, 'on'); + sinon.spy(mock, 'off'); + sinon.spy(mock, 'sortable'); + view.makeSortable(); + }); + afterEach(function() { + window.$.restore(); + mock.on.restore(); + mock.off.restore(); + mock.sortable.restore(); + }); + + it("on() should be called", function() { + expect(mock.on.calledWith('DOMNodeInserted', '#widget_layout')).to.be.true; + }); + + it("sortable() should be called", function() { + expect(mock.sortable.calledOnce).to.be.true; + }); + + it("off() should be called", function() { + expect(mock.off.calledWith('DOMNodeInserted', '#widget_layout')).to.be.true; + }); + }); + + describe('#didInsertElement', function () { + + beforeEach(function () { + sinon.stub(view, 'constructGraphObjects', Em.K); + this.mock = sinon.stub(App, 'get'); + sinon.stub(view, 'getServiceModel'); + sinon.stub(view.get('controller'), 'getActiveWidgetLayout'); + sinon.stub(view.get('controller'), 'loadWidgetLayouts'); + sinon.stub(view, 'makeSortable'); + sinon.stub(view, 'addWidgetTooltip'); + + }); + + afterEach(function () { + view.constructGraphObjects.restore(); + this.mock.restore(); + view.getServiceModel.restore(); + view.get('controller').getActiveWidgetLayout.restore(); + view.get('controller').loadWidgetLayouts.restore(); + view.makeSortable.restore(); + view.addWidgetTooltip.restore(); + }); + + it("getServiceModel should be called", function() { + view.didInsertElement(); + expect(view.getServiceModel.calledOnce).to.be.true; + }); + it("addWidgetTooltip should be called", function() { + view.didInsertElement(); + expect(view.addWidgetTooltip.calledOnce).to.be.true; + }); + it("makeSortable should be called", function() { + view.didInsertElement(); + expect(view.makeSortable.calledOnce).to.be.true; + }); + it("getActiveWidgetLayout should be called", function() { + view.didInsertElement(); + expect(view.get('controller').getActiveWidgetLayout.calledOnce).to.be.true; + }); + + describe("serviceName is null, metrics not supported, widgets not supported", function() { + beforeEach(function () { + view.set('controller.content.serviceName', null); + this.mock.returns(false); + view.didInsertElement(); + }); + + it("loadWidgetLayouts should not be called", function() { + expect(view.get('controller').loadWidgetLayouts.called).to.be.false; + }); + it("constructGraphObjects should not be called", function() { + expect(view.constructGraphObjects.called).to.be.false; + }); + }); + + describe("serviceName is set, metrics is supported, widgets is supported", function() { + beforeEach(function () { + view.set('controller.content.serviceName', 'S1'); + this.mock.returns(true); + view.didInsertElement(); + }); + + it("loadWidgetLayouts should be called", function() { + expect(view.get('controller').loadWidgetLayouts.calledOnce).to.be.true; + }); + it("constructGraphObjects should be called", function() { + expect(view.constructGraphObjects.calledOnce).to.be.true; + }); + }); + }); + + describe("#addWidgetTooltip()", function() { + var mock = { + hoverIntent: Em.K + }; + + beforeEach(function() { + sinon.stub(Em.run, 'later', function(arg1, callback) { + callback(); + }); + sinon.stub(App, 'tooltip'); + sinon.stub(window, '$').returns(mock); + sinon.spy(mock, 'hoverIntent'); + view.addWidgetTooltip(); + }); + afterEach(function() { + Em.run.later.restore(); + App.tooltip.restore(); + window.$.restore(); + mock.hoverIntent.restore(); + }); + + it("Em.run.later should be called", function() { + expect(Em.run.later.calledOnce).to.be.true; + }); + it("App.tooltip should be called", function() { + expect(App.tooltip.calledOnce).to.be.true; + }); + it("hoverIntent should be called", function() { + expect(mock.hoverIntent.calledOnce).to.be.true; + }); + }); + +}); \ No newline at end of file
