Merge "Add new resource to return a list of unique metric names"

This commit is contained in:
Jenkins
2015-03-25 15:08:22 +00:00
committed by Gerrit Code Review
8 changed files with 353 additions and 59 deletions

View File

@@ -90,9 +90,9 @@ Document Version: v2.0
- [Status Code](#status-code-2)
- [Response Body](#response-body-4)
- [Response Examples](#response-examples-3)
- [Statistics](#statistics)
- [List statistics](#list-statistics)
- [GET /v2.0/metrics/statistics](#get-v20metricsstatistics)
- [Metric Names](#metric-names)
- [List names](#list-names)
- [GET /v2.0/metrics/names](#get-v20metricsnames)
- [Headers](#headers-5)
- [Path Parameters](#path-parameters-5)
- [Query Parameters](#query-parameters-5)
@@ -102,9 +102,9 @@ Document Version: v2.0
- [Status Code](#status-code-3)
- [Response Body](#response-body-5)
- [Response Examples](#response-examples-4)
- [Notification Methods](#notification-methods)
- [Create Notification Method](#create-notification-method)
- [POST /v2.0/notification-methods](#post-v20notification-methods)
- [Statistics](#statistics)
- [List statistics](#list-statistics)
- [GET /v2.0/metrics/statistics](#get-v20metricsstatistics)
- [Headers](#headers-6)
- [Path Parameters](#path-parameters-6)
- [Query Parameters](#query-parameters-6)
@@ -114,8 +114,9 @@ Document Version: v2.0
- [Status Code](#status-code-4)
- [Response Body](#response-body-6)
- [Response Examples](#response-examples-5)
- [List Notification Methods](#list-notification-methods)
- [GET /v2.0/notification-methods](#get-v20notification-methods)
- [Notification Methods](#notification-methods)
- [Create Notification Method](#create-notification-method)
- [POST /v2.0/notification-methods](#post-v20notification-methods)
- [Headers](#headers-7)
- [Path Parameters](#path-parameters-7)
- [Query Parameters](#query-parameters-7)
@@ -125,8 +126,8 @@ Document Version: v2.0
- [Status Code](#status-code-5)
- [Response Body](#response-body-7)
- [Response Examples](#response-examples-6)
- [Get Notification Method](#get-notification-method)
- [GET /v2.0/notification-methods/{notification_method_id}](#get-v20notification-methodsnotification_method_id)
- [List Notification Methods](#list-notification-methods)
- [GET /v2.0/notification-methods](#get-v20notification-methods)
- [Headers](#headers-8)
- [Path Parameters](#path-parameters-8)
- [Query Parameters](#query-parameters-8)
@@ -136,8 +137,8 @@ Document Version: v2.0
- [Status Code](#status-code-6)
- [Response Body](#response-body-8)
- [Response Examples](#response-examples-7)
- [Update Notification Method](#update-notification-method)
- [PUT /v2.0/notification-methods/{notification_method_id}](#put-v20notification-methodsnotification_method_id)
- [Get Notification Method](#get-notification-method)
- [GET /v2.0/notification-methods/{notification_method_id}](#get-v20notification-methodsnotification_method_id)
- [Headers](#headers-9)
- [Path Parameters](#path-parameters-9)
- [Query Parameters](#query-parameters-9)
@@ -147,8 +148,8 @@ Document Version: v2.0
- [Status Code](#status-code-7)
- [Response Body](#response-body-9)
- [Response Examples](#response-examples-8)
- [Delete Notification Method](#delete-notification-method)
- [DELETE /v2.0/notification-methods/{notification_method_id}](#delete-v20notification-methodsnotification_method_id)
- [Update Notification Method](#update-notification-method)
- [PUT /v2.0/notification-methods/{notification_method_id}](#put-v20notification-methodsnotification_method_id)
- [Headers](#headers-10)
- [Path Parameters](#path-parameters-10)
- [Query Parameters](#query-parameters-10)
@@ -157,9 +158,9 @@ Document Version: v2.0
- [Response](#response-10)
- [Status Code](#status-code-8)
- [Response Body](#response-body-10)
- [Alarm Definitions](#alarm-definitions)
- [Create Alarm Definition](#create-alarm-definition)
- [POST /v2.0/alarm-definitions](#post-v20alarm-definitions)
- [Response Examples](#response-examples-9)
- [Delete Notification Method](#delete-notification-method)
- [DELETE /v2.0/notification-methods/{notification_method_id}](#delete-v20notification-methodsnotification_method_id)
- [Headers](#headers-11)
- [Path Parameters](#path-parameters-11)
- [Query Parameters](#query-parameters-11)
@@ -168,9 +169,9 @@ Document Version: v2.0
- [Response](#response-11)
- [Status Code](#status-code-9)
- [Response Body](#response-body-11)
- [Response Examples](#response-examples-9)
- [List Alarm Definitions](#list-alarm-definitions)
- [GET /v2.0/alarm-definitions](#get-v20alarm-definitions)
- [Alarm Definitions](#alarm-definitions)
- [Create Alarm Definition](#create-alarm-definition)
- [POST /v2.0/alarm-definitions](#post-v20alarm-definitions)
- [Headers](#headers-12)
- [Path Parameters](#path-parameters-12)
- [Query Parameters](#query-parameters-12)
@@ -180,29 +181,29 @@ Document Version: v2.0
- [Status Code](#status-code-10)
- [Response Body](#response-body-12)
- [Response Examples](#response-examples-10)
- [Get Alarm Definition](#get-alarm-definition)
- [GET /v2.0/alarm-definitions/{alarm_definition_id}](#get-v20alarm-definitionsalarm_definition_id)
- [List Alarm Definitions](#list-alarm-definitions)
- [GET /v2.0/alarm-definitions](#get-v20alarm-definitions)
- [Headers](#headers-13)
- [Path Parameters](#path-parameters-13)
- [Query Parameters](#query-parameters-13)
- [Request Body](#request-body-13)
- [Request Examples](#request-examples-13)
- [Response](#response-13)
- [Status Code](#status-code-11)
- [Response Body](#response-body-13)
- [Response Examples](#response-examples-11)
- [Update Alarm Definition](#update-alarm-definition)
- [PUT /v2.0/alarm-definitions/{alarm_definition_id}](#put-v20alarm-definitionsalarm_definition_id)
- [Get Alarm Definition](#get-alarm-definition)
- [GET /v2.0/alarm-definitions/{alarm_definition_id}](#get-v20alarm-definitionsalarm_definition_id)
- [Headers](#headers-14)
- [Path Parameters](#path-parameters-14)
- [Query Parameters](#query-parameters-14)
- [Request Body](#request-body-14)
- [Request Examples](#request-examples-13)
- [Response](#response-14)
- [Status Code](#status-code-12)
- [Response Body](#response-body-14)
- [Response Examples](#response-examples-12)
- [Patch Alarm Definition](#patch-alarm-definition)
- [PATCH /v2.0/alarm-definitions/{alarm_definition_id}](#patch-v20alarm-definitionsalarm_definition_id)
- [Update Alarm Definition](#update-alarm-definition)
- [PUT /v2.0/alarm-definitions/{alarm_definition_id}](#put-v20alarm-definitionsalarm_definition_id)
- [Headers](#headers-15)
- [Path Parameters](#path-parameters-15)
- [Query Parameters](#query-parameters-15)
@@ -212,8 +213,8 @@ Document Version: v2.0
- [Status Code](#status-code-13)
- [Response Body](#response-body-15)
- [Response Examples](#response-examples-13)
- [Delete Alarm Definition](#delete-alarm-definition)
- [DELETE /v2.0/alarm-definitions/{alarm_definition_id}](#delete-v20alarm-definitionsalarm_definition_id)
- [Patch Alarm Definition](#patch-alarm-definition)
- [PATCH /v2.0/alarm-definitions/{alarm_definition_id}](#patch-v20alarm-definitionsalarm_definition_id)
- [Headers](#headers-16)
- [Path Parameters](#path-parameters-16)
- [Query Parameters](#query-parameters-16)
@@ -222,8 +223,9 @@ Document Version: v2.0
- [Response](#response-16)
- [Status Code](#status-code-14)
- [Response Body](#response-body-16)
- [List Alarms](#list-alarms)
- [GET /v2.0/alarms](#get-v20alarms)
- [Response Examples](#response-examples-14)
- [Delete Alarm Definition](#delete-alarm-definition)
- [DELETE /v2.0/alarm-definitions/{alarm_definition_id}](#delete-v20alarm-definitionsalarm_definition_id)
- [Headers](#headers-17)
- [Path Parameters](#path-parameters-17)
- [Query Parameters](#query-parameters-17)
@@ -232,19 +234,19 @@ Document Version: v2.0
- [Response](#response-17)
- [Status Code](#status-code-15)
- [Response Body](#response-body-17)
- [Response Examples](#response-examples-14)
- [List Alarms State History](#list-alarms-state-history)
- [GET /v2.0/alarms/state-history](#get-v20alarmsstate-history)
- [List Alarms](#list-alarms)
- [GET /v2.0/alarms](#get-v20alarms)
- [Headers](#headers-18)
- [Path Parameters](#path-parameters-18)
- [Query Parameters](#query-parameters-18)
- [Request Body](#request-body-18)
- [Request Examples](#request-examples-17)
- [Response](#response-18)
- [Status Code](#status-code-16)
- [Response Body](#response-body-18)
- [Response Examples](#response-examples-15)
- [Get Alarm](#get-alarm)
- [GET /v2.0/alarms/{alarm_id}](#get-v20alarmsalarm_id)
- [List Alarms State History](#list-alarms-state-history)
- [GET /v2.0/alarms/state-history](#get-v20alarmsstate-history)
- [Headers](#headers-19)
- [Path Parameters](#path-parameters-19)
- [Query Parameters](#query-parameters-19)
@@ -253,19 +255,18 @@ Document Version: v2.0
- [Status Code](#status-code-17)
- [Response Body](#response-body-19)
- [Response Examples](#response-examples-16)
- [Update Alarm](#update-alarm)
- [PUT /v2.0/alarms/{alarm_id}](#put-v20alarmsalarm_id)
- [Get Alarm](#get-alarm)
- [GET /v2.0/alarms/{alarm_id}](#get-v20alarmsalarm_id)
- [Headers](#headers-20)
- [Path Parameters](#path-parameters-20)
- [Query Parameters](#query-parameters-20)
- [Request Body](#request-body-20)
- [Request Examples](#request-examples-17)
- [Response](#response-20)
- [Status Code](#status-code-18)
- [Response Body](#response-body-20)
- [Response Examples](#response-examples-17)
- [Patch Alarm](#patch-alarm)
- [PATCH /v2.0/alarms/{alarm_id}](#patch-v20alarmsalarm_id)
- [Update Alarm](#update-alarm)
- [PUT /v2.0/alarms/{alarm_id}](#put-v20alarmsalarm_id)
- [Headers](#headers-21)
- [Path Parameters](#path-parameters-21)
- [Query Parameters](#query-parameters-21)
@@ -275,8 +276,8 @@ Document Version: v2.0
- [Status Code](#status-code-19)
- [Response Body](#response-body-21)
- [Response Examples](#response-examples-18)
- [Delete Alarm](#delete-alarm)
- [DELETE /v2.0/alarms/{alarm_id}](#delete-v20alarmsalarm_id)
- [Patch Alarm](#patch-alarm)
- [PATCH /v2.0/alarms/{alarm_id}](#patch-v20alarmsalarm_id)
- [Headers](#headers-22)
- [Path Parameters](#path-parameters-22)
- [Query Parameters](#query-parameters-22)
@@ -285,17 +286,28 @@ Document Version: v2.0
- [Response](#response-22)
- [Status Code](#status-code-20)
- [Response Body](#response-body-22)
- [List Alarm State History](#list-alarm-state-history)
- [GET /v2.0/alarms/{alarm_id}/state-history](#get-v20alarmsalarm_idstate-history)
- [Response Examples](#response-examples-19)
- [Delete Alarm](#delete-alarm)
- [DELETE /v2.0/alarms/{alarm_id}](#delete-v20alarmsalarm_id)
- [Headers](#headers-23)
- [Path Parameters](#path-parameters-23)
- [Query Parameters](#query-parameters-23)
- [Request Body](#request-body-23)
- [Request Data](#request-data)
- [Request Examples](#request-examples-20)
- [Response](#response-23)
- [Status Code](#status-code-21)
- [Response Body](#response-body-23)
- [Response Examples](#response-examples-19)
- [List Alarm State History](#list-alarm-state-history)
- [GET /v2.0/alarms/{alarm_id}/state-history](#get-v20alarmsalarm_idstate-history)
- [Headers](#headers-24)
- [Path Parameters](#path-parameters-24)
- [Query Parameters](#query-parameters-24)
- [Request Body](#request-body-24)
- [Request Data](#request-data)
- [Response](#response-24)
- [Status Code](#status-code-22)
- [Response Body](#response-body-24)
- [Response Examples](#response-examples-20)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -1104,6 +1116,72 @@ Returns a JSON object with a 'links' array of links and an 'elements' array of m
```
___
# Metric Names
Operations for accessing names of metrics.
## List names
Get names for metrics.
### GET /v2.0/metrics/names
#### Headers
* X-Auth-Token (string, required) - Keystone auth token
* Accept (string) - application/json
#### Path Parameters
None.
#### Query Parameters
* dimensions (string, optional) - A dictionary to filter metrics by specified as a comma separated array of (key, value) pairs as `key1:value1,key2:value2, ...`
* offset (integer, optional)
* limit (integer, optional)
#### Request Body
None.
#### Request Examples
```
GET /v2.0/metrics/names HTTP/1.1
Host: 192.168.10.4:8080
Content-Type: application/json
X-Auth-Token: 2b8882ba2ec44295bf300aecb2caa4f7
Cache-Control: no-cache
```
### Response
#### Status Code
* 200 - OK
#### Response Body
Returns a JSON object with a 'links' array of links and an 'elements' array of metric name objects for each unique metric name (not including dimensions) with the following fields:
* name (string(255)) - A name of a metric.
#### Response Examples
```
{
"elements": [
{
"name":"name1"
},
{
"name":"name2"
}
],
"links": [
{
"rel": "self",
"href": "http://192.168.10.4:8080/v2.0/metrics/names?offset=tenantId%3region%26name1%26dimensionKey1%3DdimensionValue1%26dimensionKey2%3DdimensionValue2"
},
{
"rel": "next"
"href": http://192.168.10.4:8080/v2.0/metrics/names?offset=tenantId%3region%26name3%26dimensionKey1%3DdimensionValue1%26dimensionKey2%3DdimensionValue2
}
]
}
```
___
# Statistics
Operations for calculating statistics of metrics.

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* 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

View File

@@ -22,10 +22,13 @@ import java.util.Map;
* Repository for metrics.
*/
public interface MetricDefinitionRepo {
/**
* Finds metrics for the given criteria.
*/
List<MetricDefinition> find(String tenantId, String name, Map<String, String> dimensions,
String offset, int limit)
throws Exception;
List<MetricName> findNames(String tenantId, Map<String, String> dimensions, String offset, int limit) throws Exception;
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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. See the License for the specific language governing permissions and limitations under
* the License.
*/
package monasca.api.domain.model.metric;
import com.codahale.metrics.Metric;
import monasca.common.model.domain.common.AbstractEntity;
public class MetricName extends AbstractEntity implements Comparable {
private String id;
private String name;
public MetricName(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MetricName other = (MetricName) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
public String getId() {return id;}
public String getName() {return name;}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public void setId(String id) {this.id = id;}
public void setName(String name) {this.name = name;}
@Override
public int compareTo(Object o) {
MetricName other = (MetricName) o;
return this.name.compareTo(other.name);
}
}

View File

@@ -21,13 +21,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import monasca.api.ApiConfig;
import monasca.api.domain.model.common.Paged;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.domain.model.metric.MetricName;
import monasca.common.model.metric.MetricDefinition;
import static monasca.api.infrastructure.persistence.influxdb.InfluxV8Utils.buildSerieNameRegex;
@@ -81,7 +85,6 @@ public class InfluxV8MetricDefinitionRepo implements MetricDefinitionRepo {
String encodedMetricName = (String) point.get("name");
if (offset != null) {
if (encodedMetricName.compareTo(decodedOffset) <= 0) {
continue;
@@ -114,4 +117,62 @@ public class InfluxV8MetricDefinitionRepo implements MetricDefinitionRepo {
}
return metricDefinitionList;
}
public List<MetricName> findNames(String tenantId, Map<String, String> dimensions,
String offset, int limit) throws Exception {
// Limit is not implemented for Influxdb V8.
String serieNameRegex = buildSerieNameRegex(tenantId, config.region, null, dimensions);
String query = String.format("list series /%1$s/", serieNameRegex);
logger.debug("Query string: {}", query);
List<Serie>
result =
this.influxDB.Query(this.config.influxDB.getName(), query, TimeUnit.SECONDS);
return buildMetricNameList(result, offset);
}
private List<MetricName> buildMetricNameList(List<Serie> result, String offset) throws Exception {
// offset comes in as url encoded.
String decodedOffset = null;
if (offset != null) {
decodedOffset = urlDecodeUTF8(offset);
}
Set<MetricName> metricNameSet = new TreeSet<>();
for (Serie serie : result) {
for (Map<String, Object> point : serie.getRows()) {
String encodedMetricName = (String) point.get("name");
if (offset != null) {
if (encodedMetricName.compareTo(decodedOffset) <= 0) {
continue;
}
}
InfluxV8Utils.SerieNameDecoder serieNameDecoder;
try {
serieNameDecoder = new InfluxV8Utils.SerieNameDecoder(encodedMetricName);
} catch (InfluxV8Utils.SerieNameDecodeException e) {
logger.warn("Dropping series name that is not decodable: {}", point.get("name"), e);
continue;
}
MetricName metricName = new MetricName(urlEncodeUTF8(encodedMetricName),
serieNameDecoder.getMetricName());
metricNameSet.add(metricName);
if (offset != null) {
if (metricNameSet.size() >= Paged.LIMIT) {
return new ArrayList<>(metricNameSet);
}
}
}
}
return new ArrayList<>(metricNameSet);
}
}

View File

@@ -27,6 +27,7 @@ import java.util.Map;
import monasca.api.ApiConfig;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.domain.model.metric.MetricName;
import monasca.common.model.metric.MetricDefinition;
@@ -86,15 +87,14 @@ public class InfluxV9MetricDefinitionRepo implements MetricDefinitionRepo {
int startIndex = this.influxV9Utils.startIndex(offset);
String
q =
String.format("show series %1$s where %2$s %3$s %4$s %5$s %6$s",
this.influxV9Utils.namePart(name, false),
this.influxV9Utils.tenantIdPart(tenantId),
this.influxV9Utils.regionPart(this.region),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.limitPart(limit),
this.influxV9Utils.offsetPart(startIndex));
String q = String.format("show series %1$s "
+ "where %2$s %3$s %4$s %5$s %6$s",
this.influxV9Utils.namePart(name, false),
this.influxV9Utils.tenantIdPart(tenantId),
this.influxV9Utils.regionPart(this.region),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.limitPart(limit),
this.influxV9Utils.offsetPart(startIndex));
logger.debug("Metric definition query: {}", q);
@@ -109,7 +109,32 @@ public class InfluxV9MetricDefinitionRepo implements MetricDefinitionRepo {
return metricDefinitionList;
}
@Override
public List<MetricName> findNames(String tenantId, Map<String, String> dimensions,
String offset, int limit) throws Exception {
int startIndex = this.influxV9Utils.startIndex(offset);
String q = String.format("show measurements "
+ "where %1$s %2$s %3$s %4$s %5$s",
this.influxV9Utils.tenantIdPart(tenantId),
this.influxV9Utils.regionPart(this.region),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.limitPart(limit),
this.influxV9Utils.offsetPart(startIndex));
logger.debug("Metric name query: {}", q);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<MetricName> metricNameList = metricNameList(series, startIndex);
logger.debug("Found {} metric definitions matching query", metricNameList.size());
return metricNameList;
}
private List<MetricDefinition> metricDefinitionList(Series series, int startIndex) {
@@ -134,6 +159,26 @@ public class InfluxV9MetricDefinitionRepo implements MetricDefinitionRepo {
return metricDefinitionList;
}
private List<MetricName> metricNameList(Series series, int startIndex) {
List<MetricName> metricNameList = new ArrayList<>();
if (!series.isEmpty()) {
int index = startIndex;
Serie serie = series.getSeries()[0];
for (String[] values : serie.getValues()) {
MetricName m =
new MetricName(String.valueOf(index++), values[0]);
metricNameList.add(m);
}
}
return metricNameList;
}
private Map<String, String> dims(String[] vals, String[] cols) {
Map<String, String> dims = new HashMap<>();

View File

@@ -26,9 +26,11 @@ import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.Query;
import monasca.api.domain.model.metric.MetricName;
import monasca.common.model.metric.MetricDefinition;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.infrastructure.persistence.DimensionQueries;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
/**
* Vertica metric definition repository implementation.
@@ -94,4 +96,9 @@ public class MetricDefinitionVerticaRepoImpl implements MetricDefinitionRepo {
return metricDefs;
}
}
@Override
public List<MetricName> findNames(String tenantId, Map<String, String> dimensions, String offset, int limit) throws Exception {
throw new NotImplementedException();
}
}

View File

@@ -49,8 +49,9 @@ import monasca.common.model.metric.Metric;
*/
@Path("/v2.0/metrics")
public class MetricResource {
private static final String MONITORING_DELEGATE_ROLE = "monitoring-delegate";
private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
private final MetricService service;
private final MetricDefinitionRepo metricRepo;
@@ -114,4 +115,22 @@ public class MetricResource {
return Links.paginate(this.persistUtils.getLimit(limit),
metricRepo.find(tenantId, name, dimensions, offset, this.persistUtils.getLimit(limit)), uriInfo);
}
@GET
@Path("/names")
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object getMetricNames(@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("dimensions") String dimensionsStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit) throws Exception {
Map<String, String>
dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
.parseAndValidateNameAndDimensions("null", dimensionsStr);
return Links.paginate(this.persistUtils.getLimit(limit),
metricRepo.findNames(tenantId, dimensions, offset, this.persistUtils.getLimit(limit)), uriInfo);
}
}