aboutsummaryrefslogtreecommitdiffstats
path: root/vespaclient-container-plugin/src/main/java/com/yahoo/document
diff options
context:
space:
mode:
authorHaakon Dybdahl <dybdahl@yahoo-inc.com>2017-03-06 09:22:16 +0100
committerHaakon Dybdahl <dybdahl@yahoo-inc.com>2017-03-06 09:22:16 +0100
commit3fcafa4cc9a2f29c417cfbda9bed0c8390ba2ab7 (patch)
tree8a297abd0b7deb4f52098033535a12f11f290cd8 /vespaclient-container-plugin/src/main/java/com/yahoo/document
parent351903b75e5cf8feaa6606c139ee56e1caeccd23 (diff)
Extend APIs with condition-not-met.
Diffstat (limited to 'vespaclient-container-plugin/src/main/java/com/yahoo/document')
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java48
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/Response.java16
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/RestUri.java33
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java11
4 files changed, 75 insertions, 33 deletions
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
index 4387f975d01..3d664d87622 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/OperationHandlerImpl.java
@@ -70,16 +70,25 @@ public class OperationHandlerImpl implements OperationHandler {
private static final int HTTP_STATUS_BAD_REQUEST = 400;
private static final int HTTP_STATUS_INSUFFICIENT_STORAGE = 507;
+ private static final int HTTP_PRE_CONDIDTION_FAILED = 412;
- private static int getHTTPStatusCode(Set<Integer> errorCodes) {
- if (errorCodes.size() == 1 && errorCodes.contains(DocumentProtocol.ERROR_NO_SPACE)) {
+ private static int getHTTPStatusCode(DocumentAccessException documentException) {
+ if (documentException.getErrorCodes().size() == 1 && documentException.getErrorCodes().contains(DocumentProtocol.ERROR_NO_SPACE)) {
return HTTP_STATUS_INSUFFICIENT_STORAGE;
}
+ if (documentException.hasConditionNotMetError()) {
+ return HTTP_PRE_CONDIDTION_FAILED;
+ }
return HTTP_STATUS_BAD_REQUEST;
}
private static Response createErrorResponse(DocumentAccessException documentException, RestUri restUri) {
- return Response.createErrorResponse(getHTTPStatusCode(documentException.getErrorCodes()), documentException.getMessage(), restUri);
+ if (documentException.hasConditionNotMetError()) {
+ return Response.createErrorResponse(getHTTPStatusCode(documentException), "Condition did not match document.",
+ restUri, RestUri.apiErrorCodes.DOCUMENT_CONDITION_NOT_MET);
+ }
+ return Response.createErrorResponse(getHTTPStatusCode(documentException), documentException.getMessage(), restUri,
+ RestUri.apiErrorCodes.DOCUMENT_EXCPETION);
}
@Override
@@ -105,7 +114,8 @@ public class OperationHandlerImpl implements OperationHandler {
throw new RestApiException(Response.createErrorResponse(
500,
"Failed during parsing of arguments for visiting: " + ExceptionUtils.getStackTrace(e),
- restUri));
+ restUri,
+ RestUri.apiErrorCodes.VISITOR_ERROR));
}
try {
return doVisit(visitorControlHandler, localDataVisitorHandler, restUri);
@@ -120,13 +130,13 @@ public class OperationHandlerImpl implements OperationHandler {
RestUri restUri) throws RestApiException {
try {
if (! visitorControlHandler.waitUntilDone(VISIT_TIMEOUT_MS)) {
- throw new RestApiException(Response.createErrorResponse(500, "Timed out", restUri));
+ throw new RestApiException(Response.createErrorResponse(500, "Timed out", restUri, RestUri.apiErrorCodes.TIME_OUT));
}
if (visitorControlHandler.getResult().code != VisitorControlHandler.CompletionCode.SUCCESS) {
- throw new RestApiException(Response.createErrorResponse(400, visitorControlHandler.getResult().toString()));
+ throw new RestApiException(Response.createErrorResponse(400, visitorControlHandler.getResult().toString(), RestUri.apiErrorCodes.VISITOR_ERROR));
}
} catch (InterruptedException e) {
- throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri, RestUri.apiErrorCodes.INTERRUPTED));
}
if (localDataVisitorHandler.getErrors().isEmpty()) {
final Optional<String> continuationToken;
@@ -138,14 +148,14 @@ public class OperationHandlerImpl implements OperationHandler {
}
return new VisitResult(continuationToken, localDataVisitorHandler.getCommaSeparatedJsonDocuments());
}
- throw new RestApiException(Response.createErrorResponse(500, localDataVisitorHandler.getErrors(), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, localDataVisitorHandler.getErrors(), restUri, RestUri.apiErrorCodes.UNSPECIFIED));
}
private void setRoute(SyncSession session, Optional<String> route) throws RestApiException {
if (! (session instanceof MessageBusSyncSession)) {
// Not sure if this ever could happen but better be safe.
throw new RestApiException(Response.createErrorResponse(
- 400, "Can not set route since the API is not using message bus."));
+ 400, "Can not set route since the API is not using message bus.", RestUri.apiErrorCodes.NO_ROUTE_WHEN_NOT_PART_OF_MESSAGEBUS));
}
((MessageBusSyncSession) session).setRoute(route.orElse("default"));
}
@@ -161,7 +171,7 @@ public class OperationHandlerImpl implements OperationHandler {
} catch (DocumentAccessException documentException) {
throw new RestApiException(createErrorResponse(documentException, restUri));
} catch (Exception e) {
- throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri, RestUri.apiErrorCodes.INTERNAL_EXCEPTION));
} finally {
syncSessions.free(syncSession);
}
@@ -176,7 +186,7 @@ public class OperationHandlerImpl implements OperationHandler {
} catch (DocumentAccessException documentException) {
throw new RestApiException(createErrorResponse(documentException, restUri));
} catch (Exception e) {
- throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri, RestUri.apiErrorCodes.INTERNAL_EXCEPTION));
} finally {
syncSessions.free(syncSession);
}
@@ -194,9 +204,13 @@ public class OperationHandlerImpl implements OperationHandler {
}
syncSession.remove(documentRemove);
} catch (DocumentAccessException documentException) {
- throw new RestApiException(Response.createErrorResponse(400, documentException.getMessage(), restUri));
+ if (documentException.hasConditionNotMetError()) {
+ throw new RestApiException(Response.createErrorResponse(412, "Condition not met: " + documentException.getMessage(),
+ restUri, RestUri.apiErrorCodes.DOCUMENT_CONDITION_NOT_MET));
+ }
+ throw new RestApiException(Response.createErrorResponse(400, documentException.getMessage(), restUri, RestUri.apiErrorCodes.DOCUMENT_EXCPETION));
} catch (Exception e) {
- throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri, RestUri.apiErrorCodes.UNSPECIFIED));
} finally {
syncSessions.free(syncSession);
}
@@ -218,7 +232,7 @@ public class OperationHandlerImpl implements OperationHandler {
return Optional.of(outputStream.toString(StandardCharsets.UTF_8.name()));
} catch (Exception e) {
- throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri, RestUri.apiErrorCodes.UNSPECIFIED));
} finally {
syncSessions.free(syncSession);
}
@@ -238,7 +252,7 @@ public class OperationHandlerImpl implements OperationHandler {
if (! wantedCluster.isPresent()) {
if (clusters.size() != 1) {
new RestApiException(Response.createErrorResponse(400, "Several clusters exist: " +
- clusterListToString(clusters) + " you must specify one.. "));
+ clusterListToString(clusters) + " you must specify one.. ", RestUri.apiErrorCodes.SEVERAL_CLUSTERS));
}
return clusterDefToRoute(clusters.get(0));
}
@@ -249,7 +263,7 @@ public class OperationHandlerImpl implements OperationHandler {
}
}
throw new RestApiException(Response.createErrorResponse(400, "Your vespa cluster contains the content clusters " +
- clusterListToString(clusters) + " not " + wantedCluster.get() + ". Please select a valid vespa cluster."));
+ clusterListToString(clusters) + " not " + wantedCluster.get() + ". Please select a valid vespa cluster.", RestUri.apiErrorCodes.MISSING_CLUSTER));
}
@@ -304,7 +318,7 @@ public class OperationHandlerImpl implements OperationHandler {
try {
params.setResumeToken(ContinuationHit.getToken(continuation.get()));
} catch (Exception e) {
- throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri));
+ throw new RestApiException(Response.createErrorResponse(500, ExceptionUtils.getStackTrace(e), restUri, RestUri.apiErrorCodes.UNSPECIFIED));
}
}
return params;
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/Response.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/Response.java
index 9c846e9ce38..cbc816b4e09 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/Response.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/Response.java
@@ -25,16 +25,18 @@ public class Response extends HttpResponse {
jsonMessage = objectNode.toString();
}
- public static Response createErrorResponse(int code, String errorMessage) {
- ObjectNode objectNode = objectMapper.createObjectNode();
- objectNode.putArray("errors").add(errorMessage);
- return new Response(code, Optional.of(objectNode), Optional.<RestUri>empty());
+ public static Response createErrorResponse(int code, String errorMessage, RestUri.apiErrorCodes errorID) {
+ return createErrorResponse(code, errorMessage, null, errorID);
}
- public static Response createErrorResponse(int code, String errorMessage, RestUri restUri) {
+ public static Response createErrorResponse(int code, String errorMessage, RestUri restUri, RestUri.apiErrorCodes errorID) {
+ ObjectNode errorNode = objectMapper.createObjectNode();
+ errorNode.put("description", errorID.name() + " " + errorMessage);
+ errorNode.put("id", errorID.value);
+
ObjectNode objectNode = objectMapper.createObjectNode();
- objectNode.putArray("errors").add(errorMessage);
- return new Response(code, Optional.of(objectNode), Optional.of(restUri));
+ objectNode.putArray("errors").add(errorNode);
+ return new Response(code, Optional.of(objectNode), Optional.ofNullable(restUri));
}
@Override
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/RestUri.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/RestUri.java
index f50e7c247b1..7b6bc4e87c5 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/RestUri.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/RestUri.java
@@ -25,6 +25,30 @@ public class RestUri {
public static final String V_1 = "v1";
public static final String ID = "id:";
+ public enum apiErrorCodes {
+ ERROR_ID_BASIC_USAGE(-1),
+ ERROR_ID_DECODING_PATH(-2),
+ VISITOR_ERROR(-3),
+ NO_ROUTE_WHEN_NOT_PART_OF_MESSAGEBUS(-4),
+ SEVERAL_CLUSTERS(-5),
+ URL_PARSING(-6),
+ INVALID_CREATE_VALUE(-7),
+ TOO_MANY_PARALLEL_REQUESTS(-8),
+ MISSING_CLUSTER(-9), INTERNAL_EXCEPTION(-9),
+ DOCUMENT_CONDITION_NOT_MET(-10),
+ DOCUMENT_EXCPETION(-11),
+ PARSER_ERROR(-11),
+ GROUP_AND_EXPRESSION_ERROR(-12),
+ TIME_OUT(-13),
+ INTERRUPTED(-14),
+ UNSPECIFIED(-15);
+
+ public final long value;
+ apiErrorCodes(long value) {
+ this.value = value;
+ }
+ }
+
/**
* Represents the "grouping" part of document id which can be used with streaming model.
*/
@@ -69,6 +93,7 @@ public class RestUri {
}
static class PathParser {
+ public static final long ERROR_ID_DECODING_PATH = -10L;
final List<String> rawParts;
final String originalPath;
int readPos = 0;
@@ -88,7 +113,7 @@ public class RestUri {
try {
return URLDecoder.decode(rawId, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
- throw new RestApiException(Response.createErrorResponse(BAD_REQUEST,"Problems decoding the URI: " + e.getMessage()));
+ throw new RestApiException(Response.createErrorResponse(BAD_REQUEST,"Problems decoding the URI: " + e.getMessage(), apiErrorCodes.ERROR_ID_DECODING_PATH));
}
}
}
@@ -119,9 +144,9 @@ public class RestUri {
private static void throwUsage(String inputPath) throws RestApiException {
throw new RestApiException(Response.createErrorResponse(BAD_REQUEST,
- "Expected:\n" +
- ".../{namespace}/{document-type}/group/{name}/[{user-specified}]\n" +
- ".../{namespace}/{document-type}/docid/[{user-specified}]\n: but got " + inputPath));
+ "Expected: " +
+ ".../{namespace}/{document-type}/group/{name}/[{user-specified}] " +
+ ".../{namespace}/{document-type}/docid/[{user-specified}] : but got " + inputPath, apiErrorCodes.ERROR_ID_BASIC_USAGE));
}
}
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
index fba32f5c1fd..66e8c6475aa 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/RestApi.java
@@ -114,7 +114,7 @@ public class RestApi extends LoggingRequestHandler {
if (threadsAvailableForApi.decrementAndGet() < 1) {
return Response.createErrorResponse(
429 /* Too Many Requests */,
- "Too many parallel requests, consider using http-vespa-java-client. Please try again later.");
+ "Too many parallel requests, consider using http-vespa-java-client. Please try again later.", RestUri.apiErrorCodes.TOO_MANY_PARALLEL_REQUESTS);
}
return handleInternal(request);
} finally {
@@ -130,13 +130,13 @@ public class RestApi extends LoggingRequestHandler {
} catch (RestApiException e) {
return e.getResponse();
} catch (Exception e2) {
- return Response.createErrorResponse(500, "Exception while parsing URI: " + e2.getMessage());
+ return Response.createErrorResponse(500, "Exception while parsing URI: " + e2.getMessage(), RestUri.apiErrorCodes.URL_PARSING);
}
Optional<Boolean> create = parseBoolean(CREATE_PARAMETER_NAME, request);
if (create == null) {
return Response.createErrorResponse(403, "Non valid value for 'create' parameter, must be empty, true, or " +
- "false: " + request.getProperty(CREATE_PARAMETER_NAME));
+ "false: " + request.getProperty(CREATE_PARAMETER_NAME), RestUri.apiErrorCodes.INVALID_CREATE_VALUE);
}
String condition = request.getProperty(CONDITION_PARAMETER_NAME);
Optional<String> route = Optional.ofNullable(request.getProperty(ROUTE_PARAMETER_NAME));
@@ -163,7 +163,7 @@ public class RestApi extends LoggingRequestHandler {
} catch (Exception e2) {
// We always blame the user. This might be a bit nasty, but the parser throws various kind of exception
// types, but with nice descriptions.
- return Response.createErrorResponse(400, e2.getMessage(), restUri);
+ return Response.createErrorResponse(400, e2.getMessage(), restUri, RestUri.apiErrorCodes.PARSER_ERROR);
}
return new Response(200, resultJson, Optional.of(restUri));
}
@@ -221,7 +221,8 @@ public class RestApi extends LoggingRequestHandler {
return Response.createErrorResponse(
400,
"Visiting does not support setting value for group/value in combination with expression, try using only expression parameter instead.",
- restUri);
+ restUri,
+ RestUri.apiErrorCodes.GROUP_AND_EXPRESSION_ERROR);
}
RestUri.Group group = restUri.getGroup().get();
if (group.name == 'n') {