diff --git a/web/app/controllers/api/v1/Metric.java b/web/app/controllers/api/v1/Metric.java index 3a5ae5a108..998a6aea4d 100644 --- a/web/app/controllers/api/v1/Metric.java +++ b/web/app/controllers/api/v1/Metric.java @@ -257,6 +257,41 @@ public class Metric extends Controller result.put("message", message); return badRequest(result); } + } + public static Result getMetricListViewByMetricId(Integer id) + { + ObjectNode result = Json.newObject(); + + result.put("status", "ok"); + result.set("nodes", MetricsDAO.getMetricListViewMetricNodesByMetricId(id)); + return ok(result); + } + + public static Result getMetricListViewDashboards() + { + ObjectNode result = Json.newObject(); + + result.put("status", "ok"); + result.set("nodes", MetricsDAO.getMetricListViewDashboardNodes()); + return ok(result); + } + + public static Result getMetricListViewGroups(String dashboard) + { + ObjectNode result = Json.newObject(); + + result.put("status", "ok"); + result.set("nodes", MetricsDAO.getMetricListViewGroupsNodes(dashboard)); + return ok(result); + } + + public static Result getMetricListViewNodes(String dashboard, String group) + { + ObjectNode result = Json.newObject(); + + result.put("status", "ok"); + result.set("nodes", MetricsDAO.getMetricListViewMetricNodes(dashboard, group)); + return ok(result); } } diff --git a/web/app/dao/MetricsDAO.java b/web/app/dao/MetricsDAO.java index 9531bd5c30..256cdfca5c 100644 --- a/web/app/dao/MetricsDAO.java +++ b/web/app/dao/MetricsDAO.java @@ -21,6 +21,7 @@ import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.primitives.Ints; +import models.MetricListViewNode; import models.TreeNode; import org.apache.commons.lang3.StringUtils; import org.springframework.dao.EmptyResultDataAccessException; @@ -80,6 +81,9 @@ public class MetricsDAO extends AbstractMySQLOpenSourceDAO "LEFT JOIN watch w ON (m.metric_id = w.item_id AND w.item_type = 'metric' AND w.user_id = ?) " + "WHERE m.metric_id = ?"; + private final static String GET_METRIC_BASIC_INFO_BY_ID = "SELECT metric_name, " + + "dashboard_name, metric_group, metric_category FROM dict_business_metric WHERE metric_id = ?"; + private final static String GET_WATCHED_METRIC_ID = "SELECT id FROM watch " + "WHERE user_id = ? and item_id = ? and item_type = 'metric'"; @@ -103,23 +107,149 @@ public class MetricsDAO extends AbstractMySQLOpenSourceDAO private final static String GET_METRIC_TREE_NODES = "SELECT DISTINCT metric_category, " + "COALESCE(metric_name, '(Other)') as metric_name, metric_id " + - "FROM dict_business_metric WHERE dashboard_name = ? and metric_group = ? order by 1"; + "FROM dict_business_metric WHERE dashboard_name = ? and metric_group = ? order by 2"; private final static String GET_METRIC_TREE_NODES_NO_DASHBOARD = "SELECT DISTINCT metric_category, " + "COALESCE(metric_name, '(Other)') as metric_name, metric_id " + - "FROM dict_business_metric WHERE dashboard_name is null and metric_group = ? order by 1"; + "FROM dict_business_metric WHERE dashboard_name is null and metric_group = ? order by 2"; private final static String GET_METRIC_TREE_NODES_NO_GROUP = "SELECT DISTINCT metric_category, " + "COALESCE(metric_name, '(Other)') as metric_name, metric_id " + - "FROM dict_business_metric WHERE dashboard_name = ? and metric_group is null order by 1"; + "FROM dict_business_metric WHERE dashboard_name = ? and metric_group is null order by 2"; private final static String GET_METRIC_TREE_NODES_NO_DASHBOARD_AND_GROUP = "SELECT DISTINCT metric_category, " + "COALESCE(metric_name, '(Other)') as metric_name, metric_id " + - "FROM dict_business_metric WHERE dashboard_name is null and metric_group is null order by 1"; + "FROM dict_business_metric WHERE dashboard_name is null and metric_group is null order by 2"; public final static String GET_METRIC_AUTO_COMPLETE_LIST = "SELECT DISTINCT metric_name " + "FROM dict_business_metric WHERE metric_name is not null and metric_name != '' ORDER by 1"; + public static JsonNode getMetricListViewDashboardNodes() + { + List dashboardList = getJdbcTemplate().queryForList(GET_METRIC_TREE_DASHBOARD_NODES, String.class); + List nodes = new ArrayList(); + if (dashboardList != null && dashboardList.size() > 0) + { + for (String dashboard : dashboardList) + { + MetricListViewNode node = new MetricListViewNode(); + node.dashboard = dashboard; + node.nodeName = dashboard; + node.nodeUrl = "#/metrics/name/" + node.dashboard + "/page/1"; + nodes.add(node); + } + } + return Json.toJson(nodes); + } + + public static JsonNode getMetricListViewGroupsNodes(String dashboard) + { + List nodes = new ArrayList(); + if (StringUtils.isBlank(dashboard)) + { + return Json.toJson(nodes); + } + List groupList = null; + if (dashboard.equalsIgnoreCase("(Other)")) + { + groupList = getJdbcTemplate().queryForList(GET_METRIC_TREE_OTHER_GROUP_NODES, String.class); + } + else + { + groupList = getJdbcTemplate().queryForList(GET_METRIC_TREE_GROUP_NODES, String.class, dashboard); + } + + if (groupList != null && groupList.size() > 0) + { + for (String group : groupList) + { + MetricListViewNode node = new MetricListViewNode(); + node.dashboard = dashboard; + node.group = group; + node.nodeName = group; + node.nodeUrl = "#/metrics/name/" + dashboard + "/" + group + "/page/1"; + nodes.add(node); + } + } + return Json.toJson(nodes); + } + + public static JsonNode getMetricListViewMetricNodes(String dashboard, String group) + { + List nodes = new ArrayList(); + if (StringUtils.isBlank(dashboard) || StringUtils.isBlank(group)) + { + return Json.toJson(nodes); + } + List> rows = null; + if (dashboard.equalsIgnoreCase("(Other)")) + { + if (group.equalsIgnoreCase("(Other)")) + { + rows = getJdbcTemplate().queryForList(GET_METRIC_TREE_NODES_NO_DASHBOARD_AND_GROUP); + } + else + { + rows = getJdbcTemplate().queryForList(GET_METRIC_TREE_NODES_NO_DASHBOARD, group); + } + + } + else + { + if (group.equalsIgnoreCase("(Other)")) + { + rows = getJdbcTemplate().queryForList(GET_METRIC_TREE_NODES_NO_GROUP, dashboard); + } + else + { + rows = getJdbcTemplate().queryForList(GET_METRIC_TREE_NODES, dashboard, group); + } + } + + if (rows != null) + { + for (Map row : rows) + { + String category = (String)row.get(MetricRowMapper.METRIC_CATEGORY_COLUMN); + String name = (String)row.get(MetricRowMapper.METRIC_NAME_COLUMN); + String title = ""; + if (StringUtils.isBlank(category)) + { + title = name; + } + else + { + title = "{" + category + "} " + name; + } + + MetricListViewNode node = new MetricListViewNode(); + node.dashboard = dashboard; + node.group = group; + node.metric = name; + node.metricId = new Long((Integer)row.get(MetricRowMapper.METRIC_ID_COLUMN)); + node.nodeName = title; + node.nodeUrl = "#/metrics/" + Long.toString(node.metricId); + nodes.add(node); + } + } + return Json.toJson(nodes); + } + + public static JsonNode getMetricListViewMetricNodesByMetricId(Integer metricId) + { + List nodes = new ArrayList(); + Metric metric = getMetricBasicInfoById(metricId); + if (StringUtils.isBlank(metric.dashboardName)) + { + metric.dashboardName = "(Other)"; + } + if (StringUtils.isBlank(metric.group)) + { + metric.group = "(Other)"; + } + return getMetricListViewMetricNodes(metric.dashboardName, metric.group); + } + public static JsonNode getMetricDashboardNodes() { List dashboardList = getJdbcTemplate().queryForList(GET_METRIC_TREE_DASHBOARD_NODES, String.class); @@ -336,7 +466,37 @@ public class MetricsDAO extends AbstractMySQLOpenSourceDAO return result; } - public static Metric getMetricByID(int id, String user) + private static Metric getMetricBasicInfoById(int id) + { + List> rows = null; + Metric metric = new Metric(); + try + { + rows = getJdbcTemplate().queryForList( + GET_METRIC_BASIC_INFO_BY_ID, + id); + if (rows != null) + { + for (Map row : rows) + { + String dashboard = (String)row.get(MetricRowMapper.METRIC_DASHBOARD_NAME_COLUMN); + String group = (String)row.get(MetricRowMapper.METRIC_GROUP_COLUMN); + metric.dashboardName = dashboard; + metric.group = group; + } + } + + } + catch(EmptyResultDataAccessException e) + { + Logger.error("Metric getMetricByID failed, id = " + id); + Logger.error("Exception = " + e.getMessage()); + } + + return metric; + } + + public static Metric getMetricByID(int id, String user) { Integer userId = UserDAO.getUserIDByUserName(user); Metric metric = null; diff --git a/web/app/models/MetricListViewNode.java b/web/app/models/MetricListViewNode.java new file mode 100644 index 0000000000..e75905f7ed --- /dev/null +++ b/web/app/models/MetricListViewNode.java @@ -0,0 +1,24 @@ +/** + * Copyright 2015 LinkedIn Corp. All rights reserved. + * + * Licensed 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. + */ +package models; + +public class MetricListViewNode { + + public String dashboard; + public String nodeName; + public String nodeUrl; + public String group; + public String metric; + public Long metricId; +} diff --git a/web/app/views/index.scala.html b/web/app/views/index.scala.html index 28b468e6ab..8847caa96c 100644 --- a/web/app/views/index.scala.html +++ b/web/app/views/index.scala.html @@ -26,8 +26,8 @@ @if(isInternal) {
-
-
+
+
}
@@ -884,6 +884,21 @@
{{#unless detailview}} +
+
+
+ +
+
+
    {{#unless first}} @@ -931,21 +946,6 @@
{{/unless}} {{#unless detailview}} -
-
-
- -
-
-
@@ -996,8 +996,22 @@ - diff --git a/web/conf/routes b/web/conf/routes index 36dbc8ea70..89da50f462 100644 --- a/web/conf/routes +++ b/web/conf/routes @@ -59,11 +59,19 @@ GET /api/v2/autocomplete/searchFlow controllers.api.v1.Search.getSea GET /api/v1/list/datasets controllers.api.v1.Dataset.getDatasetListNodes() +GET /api/v1/list/metrics controllers.api.v1.Metric.getMetricListViewDashboards() + +GET /api/v1/list/metrics/:dashboard controllers.api.v1.Metric.getMetricListViewGroups(dashboard: String) + +GET /api/v1/list/metrics/:dashboard/:group controllers.api.v1.Metric.getMetricListViewNodes(dashboard: String, group: String) + +GET /api/v1/list/metric/:id controllers.api.v1.Metric.getMetricListViewByMetricId(id: Int) + GET /api/v1/list/flows controllers.api.v1.Flow.getFlowListViewClusters() GET /api/v1/list/flows/:application controllers.api.v1.Flow.getFlowListViewProjects(application:String) -GET /api/v1/list/flows/:application/:project controllers.api.v1.Flow.getFlowListViewFlows(application:String, project:String) +GET /api/v1/list/flows/:application/:project controllers.api.v1.Flow.getFlowListViewFlows(application:String, project:String) GET /api/v1/datasets controllers.api.v1.Dataset.getPagedDatasets() diff --git a/web/public/javascripts/main.js b/web/public/javascripts/main.js index 7696084a35..f2c351648c 100644 --- a/web/public/javascripts/main.js +++ b/web/public/javascripts/main.js @@ -72,9 +72,11 @@ $('#treeviewbtn').removeClass('btn-default'); $('#listviewbtn').addClass('btn-primary'); $('#treeviewbtn').addClass('btn-default'); + $('#tree1').hide(); $('#tree2').hide(); $('#tree3').hide(); $('#datasetlist').show(); + $('#metriclist').show(); $('#flowlist').show(); }); @@ -86,7 +88,9 @@ $('#listviewbtn').addClass('btn-default'); $('#treeviewbtn').addClass('btn-primary'); $('#datasetlist').hide(); + $('#metriclist').hide(); $('#flowlist').hide(); + $('#tree1').show(); $('#tree2').show(); $('#tree3').show(); }); @@ -307,6 +311,43 @@ function renderFlowListView(nodes, flowId) } } +function renderMetricListView(nodes, metricId) +{ + var folderTemplate = ' $NODE_NAME'; + var metricTemplate = ' $NODE_NAME'; + var activeTemplate = ' $NODE_NAME'; + var obj = $('#metriclist'); + if (!obj) + return; + obj.empty(); + var activeObj; + for(var i = 0; i < nodes.length; i++) + { + if (nodes[i].metricId && nodes[i].metricId > 0) + { + if (metricId == nodes[i].metricId) + { + activeObj = obj.append(activeTemplate.replace('$NODE_NAME', nodes[i].nodeName).replace("$URL", nodes[i].nodeUrl)); + } + else + { + obj.append(metricTemplate.replace('$NODE_NAME', nodes[i].nodeName).replace("$URL", nodes[i].nodeUrl)); + } + } + else + { + obj.append(folderTemplate.replace('$NODE_NAME', nodes[i].nodeName).replace("$URL", nodes[i].nodeUrl)); + } + + } + if (activeObj) + { + var scrollToActiveNode = function() { + $("#tabSplitter").scrollTo(activeObj, 800) + } + } +} + function filterListView(category, filter) { var obj = $('#flowlist'); @@ -314,6 +355,10 @@ function filterListView(category, filter) { obj = $('#datasetlist'); } + else if (category == 'Metrics') + { + obj = $('#metriclist'); + } if (!obj || !obj.children() || obj.children().length == 0) { diff --git a/web/public/javascripts/routers/metrics.js b/web/public/javascripts/routers/metrics.js index 744d2ffa8d..9cb93bba89 100644 --- a/web/public/javascripts/routers/metrics.js +++ b/web/public/javascripts/routers/metrics.js @@ -5,6 +5,13 @@ App.MetricsRoute = Ember.Route.extend({ }, actions: { getMetrics: function() { + var listUrl = 'api/v1/list/metrics'; + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); + var url = 'api/v1/metrics?size=10&page=' + metricsController.get('model.data.page'); currentTab = 'Metrics'; updateActiveTab(); @@ -20,6 +27,12 @@ App.MetricsRoute = Ember.Route.extend({ }); App.MetricspageRoute = Ember.Route.extend({ setupController: function(controller, param) { + var listUrl = 'api/v1/list/metrics'; + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); var url = 'api/v1/metrics?size=10&page=' + param.page; currentTab = 'Metrics'; updateActiveTab(); @@ -47,6 +60,12 @@ App.MetricspageRoute = Ember.Route.extend({ }, actions: { getMetrics: function() { + var listUrl = 'api/v1/list/metrics'; + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); var url = 'api/v1/metrics?size=10&page=' + metricsController.get('model.data.page'); currentTab = 'Metrics'; updateActiveTab(); @@ -178,9 +197,25 @@ App.MetricRoute = Ember.Route.extend({ { name = params.name; } + var breadcrumbs; $.get(url, function(data) { if (data && data.status == "ok"){ controller.set("model", data.metric); + var dashboard = data.metric.dashboardName; + if (!dashboard) + { + dashboard = '(Other)'; + } + var group = data.metric.group; + if (!group) + { + group = '(Other)'; + } + breadcrumbs = [{"title":"METRICS_ROOT", "urn":"page/1"}, + {"title":dashboard, "urn":"name/" + dashboard + "/page/1"}, + {"title":group, "urn":"name/" + dashboard + "/" + group + "/page/1"}, + {"title":data.metric.name, "urn": params.id}]; + controller.set('breadcrumbs', breadcrumbs); setTimeout(initializeXEditable(id, data.metric.description, data.metric.dashboardName, @@ -194,6 +229,21 @@ App.MetricRoute = Ember.Route.extend({ else if (params && params.metric) { id = params.metric.id; + var dashboard = params.metric.dashboardName; + if (!dashboard) + { + dashboard = '(Other)'; + } + var group = params.metric.group; + if (!group) + { + group = '(Other)'; + } + breadcrumbs = [{"title":"METRICS_ROOT", "urn":"page/1"}, + {"title":name, "urn":"name/" + dashboard + "/page/1"}, + {"title":group, "urn":"name/" + dashboard + "/" + group + "/page/1"}, + {"title":params.metric.name, "urn": params.id}]; + controller.set('breadcrumbs', breadcrumbs); if (params.metric.category) { name = '{' + params.metric.category + '} ' + params.metric.name; @@ -210,6 +260,15 @@ App.MetricRoute = Ember.Route.extend({ params.metric.displayFactor, params.metric.displayFactorSym), 500); } + + + var listUrl = 'api/v1/list/metric/' + id; + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes, id); + } + }); + if (name) { findAndActiveMetricNode(name, id); @@ -218,6 +277,15 @@ App.MetricRoute = Ember.Route.extend({ }, actions: { getMetrics: function() { + var id = this.get('controller.model.id'); + var listUrl = 'api/v1/list/metrics/' + id; + + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes, id); + } + }); + var url = 'api/v1/metrics/' + this.get('controller.model.id') var _this = this currentTab = 'Metrics'; @@ -242,8 +310,16 @@ App.MetricnamepageRoute = Ember.Route.extend({ && transition.resolvedModels.metricname.name) { var name = transition.resolvedModels.metricname.name; + var listUrl = 'api/v1/list/metrics/' + name; + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); + var url = 'api/v1/metrics/name/' + name + '?page=' + params.page; - var breadcrumbs = [{"title":name, "urn":"name/" + name + "/page/1"}]; + var breadcrumbs = [{"title":"METRICS_ROOT", "urn":"page/1"}, + {"title":name, "urn":"name/" + name + "/page/1"}]; $.get(url, function(data) { if (data && data.status == "ok"){ metricsController.set('model', data); @@ -273,6 +349,13 @@ App.MetricnamepageRoute = Ember.Route.extend({ }, actions: { getMetrics: function() { + var listUrl = 'api/v1/list/metrics/' + metricsController.get('dashboard'); + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); + var url = 'api/v1/metrics/name/' + metricsController.get('dashboard') url += '?size=10&page=' + metricsController.get('model.data.page'); currentTab = 'Metrics'; @@ -306,10 +389,18 @@ App.MetricnamesubpageRoute = Ember.Route.extend({ { group = transition.resolvedModels.metricgroup.group; url += '/' + group + '?page=' + params.page; - breadcrumbs = [{"title":name, "urn":"name/" + name + "/page/1"}, + breadcrumbs = [{"title":"METRICS_ROOT", "urn":"page/1"}, + {"title":name, "urn":"name/" + name + "/page/1"}, {"title":group, "urn":"name/" + name + "/" + group + "/page/1"}]; } + var listUrl = 'api/v1/list/metrics/' + name + '/' + group; + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); + $.get(url, function(data) { if (data && data.status == "ok"){ metricsController.set('breadcrumbs', breadcrumbs); @@ -338,6 +429,13 @@ App.MetricnamesubpageRoute = Ember.Route.extend({ }, actions: { getMetrics: function() { + var listUrl = 'api/v1/list/metrics/' + metricsController.get('dashboard') + + '/' + metricsController.get('group'); + $.get(listUrl, function(data) { + if (data && data.status == "ok"){ + renderMetricListView(data.nodes); + } + }); var url = 'api/v1/metrics/name/' + metricsController.get('dashboard') url += '/' + metricsController.get('group') + '?size=10&page=' + metricsController.get('model.data.page'); currentTab = 'Metrics';