Skip to content

Commit

Permalink
Merge pull request #144 from fauna/remove-object-mapper
Browse files Browse the repository at this point in the history
Directly use InputStream for parsing response JSON.
  • Loading branch information
findgriffin authored Sep 17, 2024
2 parents ca80aef + b1a2490 commit cd5b893
Show file tree
Hide file tree
Showing 29 changed files with 530 additions and 507 deletions.
4 changes: 1 addition & 3 deletions src/main/java/com/fauna/client/FaunaClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ protected String getFaunaSecret() {
}

private static <T> Supplier<CompletableFuture<QuerySuccess<T>>> makeAsyncRequest(HttpClient client, HttpRequest request, Codec<T> codec) {
// There are other options for BodyHandlers here like ofInputStream, and ofByteArray. UTF8FaunaParser
// can be initialized with an InputStream, so we could remove the extra string conversion.
return () -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(body -> QueryResponse.handleResponse(body, codec));
return () -> client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(body -> QueryResponse.parseResponse(body, codec));
}

//region Asynchronous API
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/fauna/client/FaunaStream.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fauna.client;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fauna.codec.Codec;
Expand All @@ -20,7 +21,7 @@


public class FaunaStream<E> extends SubmissionPublisher<StreamEvent<E>> implements Processor<List<ByteBuffer>, StreamEvent<E>> {
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = new JsonFactory();
private final Codec<E> dataCodec;
private Subscription subscription;
private Subscriber<? super StreamEvent<E>> eventSubscriber;
Expand Down Expand Up @@ -58,7 +59,7 @@ public void onNext(List<ByteBuffer> buffers) {
this.buffer.add(buffers);
}
try {
JsonParser parser = mapper.getFactory().createParser(buffer);
JsonParser parser = factory.createParser(buffer);
StreamEvent<E> event = StreamEvent.parse(parser, dataCodec);
if (event.getType() == StreamEvent.EventType.ERROR) {
ErrorInfo error = event.getError();
Expand Down
40 changes: 20 additions & 20 deletions src/main/java/com/fauna/exception/AbortException.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
package com.fauna.exception;

import com.fauna.codec.CodecProvider;
import com.fauna.codec.DefaultCodecProvider;
import com.fauna.codec.UTF8FaunaParser;
import com.fauna.response.QueryFailure;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class AbortException extends ServiceException {

private Object abort = null;
private final CodecProvider provider = DefaultCodecProvider.SINGLETON;
Map<Class, Object> decoded = new HashMap<>();

public AbortException(QueryFailure response) {
super(response);
}

public Object getAbort() throws IOException {
/**
* Return the abort data as a top-level Object, mostly useful for debugging and other cases where you might
* not know what to expect back.
* @return
*/
public Object getAbort() {
return getAbort(Object.class);
}

@SuppressWarnings("unchecked")
public <T> T getAbort(Class<T> clazz) throws IOException {
if (abort != null) return (T) abort;

var abStr = this.getResponse().getAbortString();
if (abStr.isPresent()) {
var codec = provider.get(clazz);
var parser = UTF8FaunaParser.fromString(abStr.get());
abort = codec.decode(parser);
return (T) abort;
} else {
return null;
/**
* Return the abort data, decoded into the given class, or null if there was no abort data.
* @param clazz The class to decode the abort data into.
* @return The abort data, or null.
* @param <T> The type of the abort data.
*/
public <T> T getAbort(Class<T> clazz) {
if (!decoded.containsKey(clazz)) {
Object abortData = getResponse().getAbort(clazz).orElse(null);
decoded.put(clazz, abortData);
}
return (T) decoded.get(clazz);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public ConstraintFailureException(QueryFailure failure) {
super(failure);
}

public List<ConstraintFailure> getConstraintFailures() {
var cf = this.getResponse().getConstraintFailures();
return cf.orElseGet(List::of);
public ConstraintFailure[] getConstraintFailures() {
return getResponse().getConstraintFailures().orElse(null);
}
}
27 changes: 0 additions & 27 deletions src/main/java/com/fauna/exception/ErrorHandler.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.fauna.exception;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fauna.response.wire.QueryResponseWire;
import com.fauna.response.QueryFailure;

import java.io.IOException;

import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_CONFLICT;
Expand All @@ -26,30 +23,6 @@ public class ErrorHandler {
private static final String INTERNAL_ERROR = "internal_error";


/**
* Handles errors based on the HTTP status code and response body.
*
* @param statusCode The HTTP status code.
* @param response The decoded response.
* @throws AbortException
* @throws AuthenticationException
* @throws AuthorizationException
* @throws ConstraintFailureException
* @throws ContendedTransactionException
* @throws InvalidRequestException
* @throws ProtocolException
* @throws QueryCheckException
* @throws QueryRuntimeException
* @throws QueryTimeoutException
* @throws ThrottlingException
*
*/
public static void handleErrorResponse(int statusCode, QueryResponseWire response, String body) {
QueryFailure failure = new QueryFailure(statusCode, response);
handleQueryFailure(statusCode, failure);
throw new ProtocolException(statusCode, body);
}

/**
* Handles errors based on the HTTP status code and error code.
*
Expand Down
16 changes: 12 additions & 4 deletions src/main/java/com/fauna/exception/ProtocolException.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package com.fauna.exception;

import com.fauna.response.QueryFailure;

import java.text.MessageFormat;
import java.util.Optional;

public class ProtocolException extends FaunaException {
private final int statusCode;
private final String body;
private QueryFailure queryFailure;
private String body;

private static String buildMessage(int statusCode) {
return MessageFormat.format("ProtocolException HTTP {0}", statusCode);
}

public ProtocolException(Throwable exc, int statusCode, String body) {
super(buildMessage(statusCode), exc);
public ProtocolException(int statusCode, QueryFailure failure) {
super(MessageFormat.format("ProtocolException HTTP {0}", statusCode));
this.statusCode = statusCode;
this.body = body;
this.queryFailure = failure;
}

public ProtocolException(int statusCode, String body) {
Expand All @@ -29,4 +33,8 @@ public int getStatusCode() {
public String getBody() {
return this.body;
}

public Optional<QueryFailure> getQueryFailure() {
return Optional.ofNullable(this.queryFailure);
}
}
71 changes: 42 additions & 29 deletions src/main/java/com/fauna/response/ConstraintFailure.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,6 @@ public class ConstraintFailure {

private final PathElement[][] paths;

// This constructor is called by the QueryFailure constructor, which is getting deprecated.
@Deprecated
public ConstraintFailure(String message, String name, List<List<Object>> pathLists) {
this.message = message;
this.name = name;
this.paths = new PathElement[pathLists.size()][];
for (int i = 0; i < pathLists.size(); i++) {
this.paths[i] = new PathElement[pathLists.get(i).size()];
for (int j = 0; j < this.paths[i].length; j++) {
Object element = pathLists.get(i).get(j);
if (element instanceof String) {
paths[i][j] = new PathElement((String) element);
} else if (element instanceof Integer) {
paths[i][j] = new PathElement((Integer) element);
}
}
}
}

public ConstraintFailure(String message, String name, PathElement[][] paths) {
this.message = message;
this.name = name;
Expand All @@ -61,10 +42,10 @@ public boolean isString() {
}

/**
* Note that this parser does not advance the parser.
* @param parser
* @return
* @throws IOException
* Note that this parse method does not advance the parser.
* @param parser A JsonParser instance.
* @return A new PathElement.
* @throws IOException Can be thrown if e.g. stream ends.
*/
public static PathElement parse(JsonParser parser) throws IOException {
if (parser.currentToken().isNumeric()) {
Expand All @@ -89,11 +70,25 @@ public String toString() {
}
}

public static PathElement[] createPath(Object... elements) {
List<PathElement> path = new ArrayList<>();
for (Object element : elements) {
if (element instanceof String) {
path.add(new PathElement((String) element));
} else if (element instanceof Integer) {
path.add(new PathElement((Integer) element));
} else {
throw new IllegalArgumentException("Only strings and integers supported");
}
}
return path.toArray(new PathElement[0]);
}


public static class Builder {
String message = null;
String name = null;
PathElement[][] paths;
List<PathElement[]> paths = new ArrayList<>();

public Builder message(String message) {
this.message = message;
Expand All @@ -105,13 +100,14 @@ public Builder name(String name) {
return this;
}

public Builder paths(PathElement[][] paths) {
this.paths = paths;
public Builder path(PathElement[] path) {
this.paths.add(path);
return this;
}

public ConstraintFailure build() {
return new ConstraintFailure(this.message, this.name, this.paths);
PathElement[][] paths = this.paths.toArray(new PathElement[this.paths.size()][]);
return new ConstraintFailure(this.message, this.name, this.paths.isEmpty() ? null : paths);
}

}
Expand Down Expand Up @@ -143,12 +139,12 @@ public static ConstraintFailure parse(JsonParser parser) throws IOException {
while (parser.nextToken() != JsonToken.END_ARRAY) {
path.add(PathElement.parse(parser));
}
paths.add(path.toArray(new PathElement[path.size()]));
paths.add(path.toArray(new PathElement[0]));
}
builder.paths(paths.toArray(new PathElement[paths.size()][]));
} else if (firstPathToken != JsonToken.VALUE_NULL) {
throw new ClientResponseException("Constraint failure path should be array or null, got: " + firstPathToken.toString());
}
paths.forEach(builder::path);
break;
}
}
Expand Down Expand Up @@ -178,4 +174,21 @@ public Optional<List<String>> getPathStrings() {
}
}

public boolean pathsAreEqual(ConstraintFailure otherFailure) {
PathElement[][] thisArray = this.getPaths().orElse(new PathElement[0][]);
PathElement[][] otherArray = otherFailure.getPaths().orElse(new PathElement[0][]);
return Arrays.deepEquals(thisArray, otherArray);
}

public boolean equals(Object other) {
if (other instanceof ConstraintFailure) {
ConstraintFailure otherFailure = (ConstraintFailure) other;
return this.getMessage().equals(otherFailure.getMessage())
&& this.getName().equals(otherFailure.getName())
&& pathsAreEqual(otherFailure);
} else {
return false;
}
}

}
Loading

0 comments on commit cd5b893

Please sign in to comment.