Skip to content

Commit

Permalink
Merge pull request #215 from burmanm/hwkmetrics-85
Browse files Browse the repository at this point in the history
We should add REST integration tests for the new endpoint. I do not think that needs to hold up merging though, so I went ahead and created [HWKMETRICS-95](https://issues.jboss.org/browse/HWKMETRICS-95).
  • Loading branch information
jsanda committed May 14, 2015
2 parents 21211d7 + c29904b commit 1032472
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,29 @@ public void createAvailabilityMetric(@Suspended final AsyncResponse asyncRespons
Futures.addCallback(future, metricCreatedCallback);
}

@GET
@Path("/{id}")
@ApiOperation(value = "Retrieve single metric definition.", response = Metric.class)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Metric's definition was successfully retrieved."),
@ApiResponse(code = 204, message = "Query was successful, but no metrics definition is set."),
@ApiResponse(code = 500, message = "Unexpected error occurred while fetching metric's definition.",
response = ApiError.class) })
public void getAvailabilityMetric(@Suspended final AsyncResponse asyncResponse,
@HeaderParam("tenantId") String tenantId, @PathParam("id") String id) {
executeAsync(
asyncResponse,
() -> {
ListenableFuture<Optional<Metric<?>>> future = metricsService.findMetric(tenantId,
MetricType.AVAILABILITY, new MetricId(id));
return Futures.transform(future, ApiUtils.MAP_VALUE);
});
}

@GET
@Path("/{id}/tags")
@ApiOperation(value = "Retrieve tags associated with the metric definition.", response = Map.class)
@ApiOperation(value = "Retrieve tags associated with the metric definition.", response = String.class,
responseContainer = "Map")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Metric's tags were successfully retrieved."),
@ApiResponse(code = 204, message = "Query was successful, but no metrics were found."),
Expand All @@ -124,9 +144,9 @@ public void getAvailabilityMetricTags(@Suspended final AsyncResponse asyncRespon
executeAsync(
asyncResponse,
() -> {
ListenableFuture<Optional<Metric<?>>> future = metricsService.findMetric(tenantId,
MetricType.AVAILABILITY, new MetricId(id));
return Futures.transform(future, ApiUtils.MAP_VALUE);
ListenableFuture<Map<String, String>> future = metricsService.getMetricTags(tenantId,
MetricType.AVAILABILITY, new MetricId(id));
return Futures.transform(future, ApiUtils.MAP_MAP);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,29 @@ public void createGaugeMetric(@Suspended final AsyncResponse asyncResponse,
Futures.addCallback(future, metricCreatedCallback);
}

@GET
@Path("/{id}")
@ApiOperation(value = "Retrieve single metric definition.", response = Metric.class)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Metric's definition was successfully retrieved."),
@ApiResponse(code = 204, message = "Query was successful, but no metrics definition is set."),
@ApiResponse(code = 500, message = "Unexpected error occurred while fetching metric's definition.",
response = ApiError.class) })
public void getGaugeMetric(@Suspended final AsyncResponse asyncResponse,
@HeaderParam("tenantId") String tenantId, @PathParam("id") String id) {
executeAsync(
asyncResponse,
() -> {
ListenableFuture<Optional<Metric<?>>> future = metricsService.findMetric(tenantId, MetricType.GAUGE,
new MetricId(id));
return Futures.transform(future, ApiUtils.MAP_VALUE);
});
}

@GET
@Path("/{id}/tags")
@ApiOperation(value = "Retrieve tags associated with the metric definition.", response = Metric.class)
@ApiOperation(value = "Retrieve tags associated with the metric definition.", response = String.class,
responseContainer = "Map")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Metric's tags were successfully retrieved."),
@ApiResponse(code = 204, message = "Query was successful, but no metrics were found."),
Expand All @@ -128,9 +148,9 @@ public void getGaugeMetricTags(@Suspended final AsyncResponse asyncResponse,
executeAsync(
asyncResponse,
() -> {
ListenableFuture<Optional<Metric<?>>> future = metricsService.findMetric(tenantId,
MetricType.GAUGE, new MetricId(id));
return Futures.transform(future, ApiUtils.MAP_VALUE);
ListenableFuture<Map<String, String>> future = metricsService.getMetricTags(tenantId, MetricType.GAUGE,
new MetricId(id));
return Futures.transform(future, ApiUtils.MAP_MAP);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public interface MetricsService {

ListenableFuture<List<Metric<?>>> findMetrics(String tenantId, MetricType type);

ListenableFuture<Map<String, String>> getMetricTags(String tenantId, MetricType type, MetricId id);

ListenableFuture<Void> addTags(Metric metric, Map<String, String> tags);

ListenableFuture<Void> deleteTags(Metric metric, Map<String, String> tags);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public interface DataAccess {

ResultSetFuture addTagsAndDataRetention(Metric<?> metric);

ResultSetFuture getMetricTags(String tenantId, MetricType type, MetricId id, long dpart);

ResultSetFuture addTags(Metric<?> metric, Map<String, String> tags);

ResultSetFuture deleteTags(Metric<?> metric, Set<String> tags);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public class DataAccessImpl implements DataAccess {

private PreparedStatement findMetric;

private PreparedStatement getMetricTags;

private PreparedStatement addMetricTagsToDataTable;

private PreparedStatement addMetadataAndDataRetention;
Expand Down Expand Up @@ -157,6 +159,11 @@ protected void initPreparedStatements() {
"FROM data " +
"WHERE tenant_id = ? AND type = ? AND metric = ? AND interval = ? AND dpart = ?");

getMetricTags = session.prepare(
"SELECT m_tags " +
"FROM data " +
"WHERE tenant_id = ? AND type = ? AND metric = ? AND interval = ? AND dpart = ?");

addMetricTagsToDataTable = session.prepare(
"UPDATE data " +
"SET m_tags = m_tags + ? " +
Expand Down Expand Up @@ -369,6 +376,12 @@ public ResultSetFuture findMetric(String tenantId, MetricType type, MetricId id,
id.getInterval().toString(), dpart));
}

@Override
public ResultSetFuture getMetricTags(String tenantId, MetricType type, MetricId id, long dpart) {
return session.executeAsync(getMetricTags.bind(tenantId, type.getCode(), id.getName(), id.getInterval()
.toString(), dpart));
}

// This method updates the metric tags and data retention in the data table. In the
// long term after we add support for bucketing/date partitioning I am not sure that we
// will store metric tags and data retention in the data table. We would have to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,20 +487,17 @@ public ListenableFuture<Void> apply(ResultSet resultSet) {
public ListenableFuture<Optional<Metric<?>>> findMetric(final String tenantId, final MetricType type,
final MetricId id) {
ResultSetFuture future = dataAccess.findMetric(tenantId, type, id, Metric.DPART);
return Futures.transform(future, new Function<ResultSet, Optional<Metric<?>>>() {
@Override
public Optional<Metric<?>> apply(ResultSet resultSet) {
if (resultSet.isExhausted()) {
return Optional.empty();
}
Row row = resultSet.one();
if (type == MetricType.GAUGE) {
return Optional.of(new Gauge(tenantId, id, row.getMap(5, String.class, String.class),
row.getInt(6)));
} else {
return Optional.of(new Availability(tenantId, id, row.getMap(5, String.class, String.class),
row.getInt(6)));
}
return Futures.transform(future, (ResultSet resultSet) -> {
if (resultSet.isExhausted()) {
return Optional.empty();
}
Row row = resultSet.one();
if (type == MetricType.GAUGE) {
return Optional.of(new Gauge(tenantId, id, row.getMap(5, String.class, String.class),
row.getInt(6)));
} else {
return Optional.of(new Availability(tenantId, id, row.getMap(5, String.class, String.class),
row.getInt(6)));
}
}, metricsTasks);
}
Expand All @@ -511,10 +508,20 @@ public ListenableFuture<List<Metric<?>>> findMetrics(String tenantId, MetricType
return Futures.transform(future, new MetricsIndexMapper(tenantId, type), metricsTasks);
}

@Override
public ListenableFuture<Map<String, String>> getMetricTags(String tenantId, MetricType type, MetricId id) {
ResultSetFuture metricTags = dataAccess.getMetricTags(tenantId, type, id, Metric.DPART);
return Futures.transform(metricTags, (ResultSet input) -> {
if (input.isExhausted()) {
return Collections.EMPTY_MAP;
}
return input.one().getMap(0, String.class, String.class);
}, metricsTasks);
}

// Adding/deleting metric tags currently involves writing to three tables - data,
// metrics_idx, and metrics_tags_idx. It might make sense to refactor tag related
// functionality into a separate class.

@Override
public ListenableFuture<Void> addTags(Metric metric, Map<String, String> tags) {
List<ResultSetFuture> insertFutures = asList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public ResultSetFuture findMetric(String tenantId, MetricType type, MetricId id,
return delegate.findMetric(tenantId, type, id, dpart);
}

@Override
public ResultSetFuture getMetricTags(String tenantId, MetricType type, MetricId id, long dpart) {
return delegate.getMetricTags(tenantId, type, id, dpart);
}

@Override
public ResultSetFuture addTagsAndDataRetention(Metric metric) {
return delegate.addTagsAndDataRetention(metric);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.hawkular.metrics.rest

import groovy.json.JsonOutput

import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull

Expand Down Expand Up @@ -67,6 +69,9 @@ class TagsITest extends RESTTest {

response = hawkularMetrics.get(path: "availability/missing/tags", headers: ["tenantId": tenantId])
assertEquals("Expected a 204 response when the availability metric is not found", 204, response.status)

response = hawkularMetrics.get(path: "gauges/missing", headers: ["tenantId": tenantId])
assertEquals("Expected a 204 response when the gauge metric is not found", 204, response.status)
}

@Test
Expand All @@ -80,6 +85,15 @@ class TagsITest extends RESTTest {
], headers: ["tenantId": tenantId])
assertEquals(201, response.status)

// Fetch the metric
response = hawkularMetrics.get(path: "gauges/N1", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals([
tenantId: tenantId,
id : 'N1',
tags: [' a 1 ': ' A', 'bsq d1': 'B ']
], response.data)

// Make sure we do not allow duplicates
badPost(path: "gauges", body: [id: 'N1'], headers: ["tenantId": tenantId]) { exception ->
assertEquals(409, exception.response.status)
Expand Down Expand Up @@ -118,26 +132,11 @@ class TagsITest extends RESTTest {
// Fetch gauge tags
response = hawkularMetrics.get(path: "gauges/N1/tags", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals(
[
tenantId: tenantId,
id : 'N1',
tags : [' a 1 ': ' A', 'bsq d1': 'B ']
],
response.data
)
assertEquals(' a 1 ': ' A', 'bsq d1': 'B ', response.data)

response = hawkularMetrics.get(path: "gauges/N2/tags", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals(
[
tenantId : tenantId,
id : 'N2',
tags : [a2: '2', b2: 'B2'],
dataRetention: 96
],
response.data
)
assertEquals([a2: '2', b2: 'B2'], response.data)

// Verify the response for a non-existent metric
response = hawkularMetrics.get(path: "gauges/N-doesNotExist/tags", headers: ["tenantId": tenantId])
Expand All @@ -146,26 +145,11 @@ class TagsITest extends RESTTest {
// Fetch availability metric tags
response = hawkularMetrics.get(path: "availability/A1/tags", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals(
[
tenantId: tenantId,
id : 'A1',
tags : [a2: '2', b2: '2']
],
response.data
)
assertEquals([a2: '2', b2: '2'], response.data)

response = hawkularMetrics.get(path: "availability/A2/tags", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals(
[
tenantId : tenantId,
id : 'A2',
tags : [a22: '22', b22: '22'],
dataRetention: 48
],
response.data
)
assertEquals([a22: '22', b22: '22'], response.data)

// Verify the response for a non-existent metric
response = hawkularMetrics.get(path: "gauges/A-doesNotExist/tags", headers: ["tenantId": tenantId])
Expand All @@ -178,27 +162,13 @@ class TagsITest extends RESTTest {
// Fetch the updated tags
response = hawkularMetrics.get(path: "gauges/N1/tags", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals(
[
tenantId: tenantId,
id : 'N1',
tags : [' a 1 ': ' A', a1: 'one', a2: '2', b1: 'B', 'bsq d1': 'B ']
],
response.data
)
assertEquals([' a 1 ': ' A', a1: 'one', a2: '2', b1: 'B', 'bsq d1': 'B '], response.data)

// Delete a gauge metric tag
response = hawkularMetrics.delete(path: "gauges/N1/tags/a2:2,b1:B", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
response = hawkularMetrics.get(path: "gauges/N1/tags", headers: ["tenantId": tenantId])
assertEquals(
[
tenantId: tenantId,
id : 'N1',
tags : [' a 1 ': ' A', a1: 'one', 'bsq d1': 'B ']
],
response.data
)
assertEquals([' a 1 ': ' A', a1: 'one', 'bsq d1': 'B '], response.data)

// Update the availability metric data
response = hawkularMetrics.put(path: "availability/A1/tags", body: [a2: 'two', a3: 'THREE'], headers: ["tenantId": tenantId])
Expand All @@ -207,27 +177,13 @@ class TagsITest extends RESTTest {
// Fetch the updated tags
response = hawkularMetrics.get(path: "availability/A1/tags", headers: ["tenantId": tenantId])
assertEquals(200, response.status)
assertEquals(
[
tenantId: tenantId,
id : 'A1',
tags : [a2: 'two', a3: 'THREE', b2: '2']
],
response.data
)
assertEquals([a2: 'two', a3: 'THREE', b2: '2'], response.data)

// delete an availability metric tag
response = hawkularMetrics.delete(path: "availability/A1/tags/a2:two,b2:2", headers: ["tenantId": tenantId])
assertEquals(200, response.status)

response = hawkularMetrics.get(path: "availability/A1/tags", headers: ["tenantId": tenantId])
assertEquals(
[
tenantId: tenantId,
id : 'A1',
tags : [a3: 'THREE']
],
response.data
)
assertEquals([a3: 'THREE'], response.data)
}
}

0 comments on commit 1032472

Please sign in to comment.