Skip to content

Commit

Permalink
HWKMETRICS-29 Create method return codes should be consistent
Browse files Browse the repository at this point in the history
  • Loading branch information
tsegismont committed Mar 13, 2015
1 parent 87a9f50 commit 0175dfc
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
import static org.hawkular.metrics.core.api.MetricsService.DEFAULT_TENANT_ID;

import java.net.URI;
import java.util.Collection;
import java.util.DoubleSummaryStatistics;
import java.util.HashMap;
Expand All @@ -48,15 +49,19 @@
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import org.hawkular.metrics.api.jaxrs.callback.NoDataCallback;
import org.hawkular.metrics.api.jaxrs.callback.SimpleDataCallback;
import org.hawkular.metrics.core.api.Availability;
import org.hawkular.metrics.core.api.AvailabilityMetric;
import org.hawkular.metrics.core.api.Counter;
import org.hawkular.metrics.core.api.Metric;
import org.hawkular.metrics.core.api.MetricAlreadyExistsException;
import org.hawkular.metrics.core.api.MetricId;
import org.hawkular.metrics.core.api.MetricType;
import org.hawkular.metrics.core.api.MetricsService;
Expand All @@ -68,6 +73,8 @@
import org.hawkular.metrics.core.impl.request.TagRequest;

import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
Expand Down Expand Up @@ -96,33 +103,95 @@ public class MetricHandler {
@ApiOperation(value = "Create numeric metric definition.", notes = "Clients are not required to explicitly create "
+ "a metric before storing data. Doing so however allows clients to prevent naming collisions and to "
+ "specify tags and data retention.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "Metric definition created successfully"),
@ApiResponse(code = 400, message = "Metric with given id already exists or request is otherwise incorrect",
response = ApiError.class),
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Metric definition created successfully"),
@ApiResponse(code = 400, message = "Missing or invalid payload"),
@ApiResponse(code = 409, message = "Numeric metric with given id already exists"),
@ApiResponse(code = 500, message = "Metric definition creation failed due to an unexpected error",
response = ApiError.class)
})
public void createNumericMetric(@Suspended final AsyncResponse asyncResponse,
@PathParam("tenantId") String tenantId, @ApiParam(required = true) NumericMetric metric) {
@PathParam("tenantId") String tenantId,
@ApiParam(required = true) NumericMetric metric,
@Context UriInfo uriInfo
) {
if (metric == null) {
Response response = Response.status(Status.BAD_REQUEST)
.entity(new ApiError("Payload is empty"))
.type(APPLICATION_JSON_TYPE)
.build();
asyncResponse.resume(response);
return;
}
metric.setTenantId(tenantId);
ListenableFuture<Void> future = metricsService.createMetric(metric);
Futures.addCallback(future, new NoDataCallback<Void>(asyncResponse));
URI created = uriInfo.getBaseUriBuilder()
.path("/{tenantId}/metrics/numeric/{id}")
.build(tenantId, metric.getId().getName());
Futures.addCallback(future, new MetricCreatedCallback(asyncResponse, created));
}

@POST
@Path("/{tenantId}/metrics/availability")
@ApiOperation(value = "Create availability metric definition. Same notes as creating numeric metric apply.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "Metric definition created successfully"),
@ApiResponse(code = 400, message = "Metric with given id already exists",
response = ApiError.class),
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Metric definition created successfully"),
@ApiResponse(code = 400, message = "Missing or invalid payload"),
@ApiResponse(code = 409, message = "Numeric metric with given id already exists"),
@ApiResponse(code = 500, message = "Metric definition creation failed due to an unexpected error",
response = ApiError.class)
})
public void createAvailabilityMetric(@Suspended final AsyncResponse asyncResponse,
@PathParam("tenantId") String tenantId, @ApiParam(required = true) AvailabilityMetric metric) {
@PathParam("tenantId") String tenantId,
@ApiParam(required = true) AvailabilityMetric metric,
@Context UriInfo uriInfo
) {
if (metric == null) {
Response response = Response.status(Status.BAD_REQUEST)
.entity(ImmutableMap.of("errorMsg", "Payload is empty"))
.type(APPLICATION_JSON_TYPE)
.build();
asyncResponse.resume(response);
return;
}
metric.setTenantId(tenantId);
ListenableFuture<Void> future = metricsService.createMetric(metric);
Futures.addCallback(future, new NoDataCallback<Void>(asyncResponse));
URI created = uriInfo.getBaseUriBuilder()
.path("/{tenantId}/metrics/availability/{id}")
.build(tenantId, metric.getId().getName());
Futures.addCallback(future, new MetricCreatedCallback(asyncResponse, created));
}

private static class MetricCreatedCallback implements FutureCallback<Void> {
private final AsyncResponse asyncResponse;
private final URI created;

public MetricCreatedCallback(AsyncResponse asyncResponse, URI created) {
this.asyncResponse = asyncResponse;
this.created = created;
}

@Override
public void onSuccess(Void result) {
asyncResponse.resume(Response.created(created).build());
}

@Override
public void onFailure(Throwable t) {
ResponseBuilder response;
String message;
if (t instanceof MetricAlreadyExistsException) {
MetricAlreadyExistsException exception = (MetricAlreadyExistsException) t;
message = "A metric with name [" + exception.getMetric().getId().getName() + "] already exists";
response = Response.status(Status.CONFLICT);
} else {
message = "Failed to create metric due to an unexpected error: "
+ Throwables.getRootCause(t).getMessage();
response = Response.serverError();
}
response.entity(new ApiError(message)).type(APPLICATION_JSON_TYPE);
asyncResponse.resume(response.build());
}
}

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;

import java.net.URI;
import java.util.Collection;
import java.util.List;

Expand All @@ -30,8 +31,10 @@
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import org.hawkular.metrics.core.api.MetricsService;
import org.hawkular.metrics.core.api.Tenant;
Expand Down Expand Up @@ -65,36 +68,48 @@ public class TenantsHandler {
@ApiOperation(value = "Create a new tenant. ", notes = "Clients are not required to create explicitly create a "
+ "tenant before starting to store metric data. It is recommended to do so however to ensure that there "
+ "are no tenant id naming collisions and to provide default data retention settings. ")
@ApiResponses(value = { @ApiResponse(code = 200, message = "Tenant has been succesfully created."),
@ApiResponse(code = 400, message = "Retention properties are invalid. ",
response = ApiError.class),
@ApiResponse(code = 409, message = "Given tenant id has already been created.",
response = ApiError.class),
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Tenant has been succesfully created."),
@ApiResponse(code = 400, message = "Missing or invalid retention properties. "),
@ApiResponse(code = 409, message = "Given tenant id has already been created."),
@ApiResponse(code = 500, message = "An unexpected error occured while trying to create a tenant.",
response = ApiError.class)
})
public void createTenant(@Suspended AsyncResponse asyncResponse, @ApiParam(required = true) Tenant params) {
public void createTenant(
@Suspended AsyncResponse asyncResponse, @ApiParam(required = true) Tenant params,
@Context UriInfo uriInfo
) {
if (params == null) {
Response response = Response.status(Status.BAD_REQUEST)
.entity(new ApiError("Payload is empty"))
.type(APPLICATION_JSON_TYPE)
.build();
asyncResponse.resume(response);
return;
}
ListenableFuture<Void> insertFuture = metricsService.createTenant(params);
URI created = uriInfo.getBaseUriBuilder().path("/tenants").build();
Futures.addCallback(insertFuture, new FutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
asyncResponse.resume(Response.ok().type(APPLICATION_JSON_TYPE).build());
asyncResponse.resume(Response.created(created).type(APPLICATION_JSON_TYPE).build());
}

@Override
public void onFailure(Throwable t) {
Response.ResponseBuilder response;
String message;
if (t instanceof TenantAlreadyExistsException) {
TenantAlreadyExistsException exception = (TenantAlreadyExistsException) t;
ApiError errors = new ApiError("A tenant with id [" + exception.getTenantId() + "] already exists");
asyncResponse.resume(Response.status(Status.CONFLICT).entity(errors).type(APPLICATION_JSON_TYPE)
.build());
return;
message = "A tenant with id [" + exception.getTenantId() + "] already exists";
response = Response.status(Status.CONFLICT);
} else {
message = "Failed to create tenant due to an unexpected error: "
+ Throwables.getRootCause(t).getMessage();
response = Response.serverError();
}
ApiError errors = new ApiError(
"Failed to create tenant due to an "
+ "unexpected error: " + Throwables.getRootCause(t).getMessage());
asyncResponse.resume(Response.status(Status.INTERNAL_SERVER_ERROR).entity(errors)
.type(APPLICATION_JSON_TYPE).build());
response.entity(new ApiError(message)).type(APPLICATION_JSON_TYPE);
asyncResponse.resume(response.build());
}
});
}
Expand Down

0 comments on commit 0175dfc

Please sign in to comment.