Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wip
Browse files Browse the repository at this point in the history
Pritham Marupaka committed Jan 8, 2025
1 parent d74dea3 commit eb93b54
Showing 9 changed files with 107 additions and 293 deletions.
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@

import com.palantir.dialogue.Response;

// TODO(pm): use the new EndpointErrorDecoder
public final class ConjureErrorDecoder implements ErrorDecoder {

@Override
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
@@ -52,7 +53,6 @@
/**
* items:
* - we don't want to use `String` for the error identifier. Let's create an `ErrorName` class.
* - re-consider using a map for the deserializersForEndpointBaseType field. is there a more direct way to get this info
*/

/** Package private internal API. */
@@ -65,7 +65,7 @@ final class ConjureBodySerDe implements BodySerDe {
private final Deserializer<Optional<InputStream>> optionalBinaryInputStreamDeserializer;
private final Deserializer<Void> emptyBodyDeserializer;
private final LoadingCache<Type, Serializer<?>> serializers;
private final LoadingCache<Type, EncodingDeserializerRegistry<?>> deserializers;
private final LoadingCache<Type, EncodingDeserializerForEndpointRegistry<?>> deserializers;
private final EmptyContainerDeserializer emptyContainerDeserializer;

/**
@@ -75,32 +75,48 @@ final class ConjureBodySerDe implements BodySerDe {
*/
ConjureBodySerDe(
List<WeightedEncoding> rawEncodings,
ErrorDecoder errorDecoder,
EmptyContainerDeserializer emptyContainerDeserializer,
CaffeineSpec cacheSpec) {
List<WeightedEncoding> encodings = decorateEncodings(rawEncodings);
this.encodingsSortedByWeight = sortByWeight(encodings);
Preconditions.checkArgument(encodings.size() > 0, "At least one Encoding is required");
// note(pm): why do the weighted encoding thing? can we just pass in the default encoding?
this.defaultEncoding = encodings.get(0).encoding();
this.emptyContainerDeserializer = emptyContainerDeserializer;
this.binaryInputStreamDeserializer = new EncodingDeserializerRegistry<>(
this.binaryInputStreamDeserializer = new EncodingDeserializerForEndpointRegistry<>(
ImmutableList.of(BinaryEncoding.INSTANCE),
errorDecoder,
emptyContainerDeserializer,
BinaryEncoding.MARKER);
this.optionalBinaryInputStreamDeserializer = new EncodingDeserializerRegistry<>(
BinaryEncoding.MARKER,
DeserializerArgs.<InputStream>builder()
.withBaseType(BinaryEncoding.MARKER)
.withExpectedResult(BinaryEncoding.MARKER)
.build());
this.optionalBinaryInputStreamDeserializer = new EncodingDeserializerForEndpointRegistry<>(
ImmutableList.of(BinaryEncoding.INSTANCE),
errorDecoder,
emptyContainerDeserializer,
BinaryEncoding.OPTIONAL_MARKER);
this.emptyBodyDeserializer = new EmptyBodyDeserializer(errorDecoder);
BinaryEncoding.OPTIONAL_MARKER,
DeserializerArgs.<Optional<InputStream>>builder()
.withBaseType(BinaryEncoding.OPTIONAL_MARKER)
.withExpectedResult(BinaryEncoding.OPTIONAL_MARKER)
.build());
this.emptyBodyDeserializer =
new EmptyBodyDeserializer(new EndpointErrorDecoder<>(Collections.emptyMap(), Optional.empty()));
// Class unloading: Not supported, Jackson keeps strong references to the types
// it sees: https://github.com/FasterXML/jackson-databind/issues/489
this.serializers = Caffeine.from(cacheSpec)
.build(type -> new EncodingSerializerRegistry<>(defaultEncoding, TypeMarker.of(type)));
this.deserializers = Caffeine.from(cacheSpec)
.build(type -> new EncodingDeserializerRegistry<>(
encodingsSortedByWeight, errorDecoder, emptyContainerDeserializer, TypeMarker.of(type)));
this.deserializers = Caffeine.from(cacheSpec).build(type -> buildCacheEntry(TypeMarker.of(type)));
}

private <T> EncodingDeserializerForEndpointRegistry<?> buildCacheEntry(TypeMarker<T> typeMarker) {
return new EncodingDeserializerForEndpointRegistry<>(
encodingsSortedByWeight,
emptyContainerDeserializer,
typeMarker,
DeserializerArgs.<T>builder()
.withBaseType(typeMarker)
.withExpectedResult(typeMarker)
.build());
}

private static List<WeightedEncoding> decorateEncodings(List<WeightedEncoding> input) {
@@ -235,108 +251,7 @@ private static final class EncodingSerializerContainer<T> {
}
}

private static final class EncodingDeserializerRegistry<T> implements Deserializer<T> {

private static final SafeLogger log = SafeLoggerFactory.get(EncodingDeserializerRegistry.class);
private final ImmutableList<EncodingDeserializerContainer<T>> encodings;
private final ErrorDecoder errorDecoder;
private final Optional<String> acceptValue;
private final Supplier<Optional<T>> emptyInstance;
private final TypeMarker<T> token;

EncodingDeserializerRegistry(
List<Encoding> encodings,
ErrorDecoder errorDecoder,
EmptyContainerDeserializer empty,
TypeMarker<T> token) {
this.encodings = encodings.stream()
.map(encoding -> new EncodingDeserializerContainer<>(encoding, token))
.collect(ImmutableList.toImmutableList());
this.errorDecoder = errorDecoder;
this.token = token;
this.emptyInstance = Suppliers.memoize(() -> empty.tryGetEmptyInstance(token));
// Encodings are applied to the accept header in the order of preference based on the provided list.
this.acceptValue =
Optional.of(encodings.stream().map(Encoding::getContentType).collect(Collectors.joining(", ")));
}

@Override
public T deserialize(Response response) {
boolean closeResponse = true;
try {
if (errorDecoder.isError(response)) {
throw errorDecoder.decode(response);
} else if (response.code() == 204) {
// TODO(dfox): what if we get a 204 for a non-optional type???
// TODO(dfox): support http200 & body=null
// TODO(dfox): what if we were expecting an empty list but got {}?
Optional<T> maybeEmptyInstance = emptyInstance.get();
if (maybeEmptyInstance.isPresent()) {
return maybeEmptyInstance.get();
}
throw new SafeRuntimeException(
"Unable to deserialize non-optional response type from 204", SafeArg.of("type", token));
}

Optional<String> contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
if (!contentType.isPresent()) {
throw new SafeIllegalArgumentException(
"Response is missing Content-Type header",
SafeArg.of("received", response.headers().keySet()));
}
Encoding.Deserializer<T> deserializer = getResponseDeserializer(contentType.get());
T deserialized = deserializer.deserialize(response.body());
// deserializer has taken on responsibility for closing the response body
closeResponse = false;
return deserialized;
} catch (IOException e) {
throw new SafeRuntimeException(
"Failed to deserialize response stream",
e,
SafeArg.of("contentType", response.getFirstHeader(HttpHeaders.CONTENT_TYPE)),
SafeArg.of("type", token));
} finally {
if (closeResponse) {
response.close();
}
}
}

@Override
public Optional<String> accepts() {
return acceptValue;
}

/** Returns the {@link EncodingDeserializerContainer} to use to deserialize the request body. */
@SuppressWarnings("ForLoopReplaceableByForEach")
// performance sensitive code avoids iterator allocation
Encoding.Deserializer<T> getResponseDeserializer(String contentType) {
for (int i = 0; i < encodings.size(); i++) {
EncodingDeserializerContainer<T> container = encodings.get(i);
if (container.encoding.supportsContentType(contentType)) {
return container.deserializer;
}
}
return throwingDeserializer(contentType);
}

private Encoding.Deserializer<T> throwingDeserializer(String contentType) {
return input -> {
try {
input.close();
} catch (RuntimeException | IOException e) {
log.warn("Failed to close InputStream", e);
}
throw new SafeRuntimeException(
"Unsupported Content-Type",
SafeArg.of("received", contentType),
SafeArg.of("supportedEncodings", encodings));
};
}
}

private static final class EncodingDeserializerForEndpointRegistry<T> implements Deserializer<T> {

private static final SafeLogger log = SafeLoggerFactory.get(EncodingDeserializerForEndpointRegistry.class);
private final ImmutableList<EncodingDeserializerContainer<? extends T>> encodings;
private final EndpointErrorDecoder<T> endpointErrorDecoder;
@@ -353,8 +268,11 @@ private static final class EncodingDeserializerForEndpointRegistry<T> implements
.map(encoding -> new EncodingDeserializerContainer<>(
encoding, deserializersForEndpoint.expectedResultType()))
.collect(ImmutableList.toImmutableList());
this.endpointErrorDecoder =
new EndpointErrorDecoder<>(deserializersForEndpoint.errorNameToTypeMarker(), encodings);
this.endpointErrorDecoder = new EndpointErrorDecoder<>(
deserializersForEndpoint.errorNameToTypeMarker(),
encodings.stream()
.filter(encoding -> encoding.supportsContentType("application/json"))
.findAny());
this.token = token;
this.emptyInstance = Suppliers.memoize(() -> empty.tryGetEmptyInstance(token));
// Encodings are applied to the accept header in the order of preference based on the provided list.
@@ -367,7 +285,6 @@ public T deserialize(Response response) {
boolean closeResponse = true;
try {
if (endpointErrorDecoder.isError(response)) {
// TODO(pm): This needs to return T for the new deserializer API, but throw an exception for the old
return endpointErrorDecoder.decode(response);
} else if (response.code() == 204) {
Optional<T> maybeEmptyInstance = emptyInstance.get();
@@ -457,19 +374,19 @@ public String toString() {
}

private static final class EmptyBodyDeserializer implements Deserializer<Void> {
private final ErrorDecoder errorDecoder;
private final EndpointErrorDecoder<?> endpointErrorDecoder;

EmptyBodyDeserializer(ErrorDecoder errorDecoder) {
this.errorDecoder = errorDecoder;
EmptyBodyDeserializer(EndpointErrorDecoder<?> endpointErrorDecoder) {
this.endpointErrorDecoder = endpointErrorDecoder;
}

@Override
@SuppressWarnings("NullAway") // empty body is a special case
public Void deserialize(Response response) {
// We should not fail if a server that previously returned nothing starts returning a response
try (Response unused = response) {
if (errorDecoder.isError(response)) {
throw errorDecoder.decode(response);
if (endpointErrorDecoder.isError(response)) {
endpointErrorDecoder.decode(response);
}
return null;
}
Original file line number Diff line number Diff line change
@@ -45,7 +45,6 @@ public final class DefaultConjureRuntime implements ConjureRuntime {
private DefaultConjureRuntime(Builder builder) {
this.bodySerDe = new ConjureBodySerDe(
builder.encodings.isEmpty() ? DEFAULT_ENCODINGS : builder.encodings,
ErrorDecoder.INSTANCE,
Encodings.emptyContainerDeserializer(),
DEFAULT_SERDE_CACHE_SPEC);
}
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
import com.palantir.conjure.java.api.errors.RemoteException;
import com.palantir.conjure.java.api.errors.SerializableError;
import com.palantir.conjure.java.api.errors.UnknownRemoteException;
import com.palantir.conjure.java.dialogue.serde.Encoding.Deserializer;
import com.palantir.conjure.java.serialization.ObjectMappers;
import com.palantir.dialogue.Response;
import com.palantir.dialogue.TypeMarker;
@@ -48,21 +49,27 @@
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

// TODO(pm): public because maybe we need to expose this in the dialogue annotations. What does that do?
// T is the base type of the endpoint response. It's a union of the result type and all of the error types.
public final class EndpointErrorDecoder<T> {
private static final SafeLogger log = SafeLoggerFactory.get(EndpointErrorDecoder.class);
private static final ObjectMapper MAPPER = ObjectMappers.newClientObjectMapper();
private final Map<String, TypeMarker<? extends T>> errorNameToTypeMap;
private final List<Encoding> encodings;

public EndpointErrorDecoder(Map<String, TypeMarker<? extends T>> errorNameToTypeMap, List<Encoding> encodings) {
this.errorNameToTypeMap = errorNameToTypeMap;
this.encodings = encodings;
private final Map<String, Encoding.Deserializer<? extends T>> errorNameToJsonDeserializerMap;

public EndpointErrorDecoder(
Map<String, TypeMarker<? extends T>> errorNameToTypeMap, Optional<Encoding> maybeJsonEncoding) {
this.errorNameToJsonDeserializerMap = maybeJsonEncoding
.<Map<String, Encoding.Deserializer<? extends T>>>map(
jsonEncoding -> errorNameToTypeMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey, entry -> jsonEncoding.deserializer(entry.getValue()))))
.orElseGet(Collections::emptyMap);
}

public boolean isError(Response response) {
@@ -74,17 +81,17 @@ public T decode(Response response) {
log.debug("Received an error response", diagnosticArgs(response));
}
try {
// this both returns an error and throws a runtime exception.
return decodeInternal(response);
} catch (Exception e) {
// TODO(pm): do we want to add the diagnostic information to the result type as well?
// TODO(pm): Diagnostic info is added for exceptions, but is not added to result types. Is this info useful
// in debugging when there's a clear error from the server?
e.addSuppressed(diagnostic(response));
throw e;
}
}

// performance sensitive code avoids iterator allocation
@SuppressWarnings({"checkstyle:CyclomaticComplexity", "ForLoopReplaceableByForEach"})
private T decodeInternal(Response response) {
Optional<RuntimeException> checkCode(Response response) {
int code = response.code();
switch (code) {
case 308:
@@ -95,7 +102,7 @@ private T decodeInternal(Response response) {
UnknownRemoteException remoteException = new UnknownRemoteException(code, "");
remoteException.initCause(
QosException.retryOther(qosReason(response), new URL(locationHeader)));
throw remoteException;
return Optional.of(remoteException);
} catch (MalformedURLException e) {
log.error(
"Failed to parse location header for QosException.RetryOther",
@@ -108,15 +115,25 @@ private T decodeInternal(Response response) {
}
break;
case 429:
throw response.getFirstHeader(HttpHeaders.RETRY_AFTER)
return Optional.of(response.getFirstHeader(HttpHeaders.RETRY_AFTER)
.map(Longs::tryParse)
.map(Duration::ofSeconds)
.map(duration -> QosException.throttle(qosReason(response), duration))
.orElseGet(() -> QosException.throttle(qosReason(response)));
.orElseGet(() -> QosException.throttle(qosReason(response))));
case 503:
throw QosException.unavailable(qosReason(response));
return Optional.of(QosException.unavailable(qosReason(response)));
}
return Optional.empty();
}

// performance sensitive code avoids iterator allocation
@SuppressWarnings({"checkstyle:CyclomaticComplexity", "ForLoopReplaceableByForEach"})
private T decodeInternal(Response response) {
Optional<RuntimeException> maybeQosException = checkCode(response);
if (maybeQosException.isPresent()) {
throw maybeQosException.get();
}
int code = response.code();
String body;
try {
body = toString(response.body());
@@ -127,27 +144,25 @@ private T decodeInternal(Response response) {
}

Optional<String> contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
// Use a factory: given contentType, create the deserailizer.
// We need Encoding.Deserializer here. That depends on the encoding.
if (contentType.isPresent() && Encodings.matchesContentType("application/json", contentType.get())) {
String jsonContentType = "application/json";
if (contentType.isPresent() && Encodings.matchesContentType(jsonContentType, contentType.get())) {
try {
JsonNode node = MAPPER.readTree(body);
if (node.get("errorName") != null) {
// TODO(pm): Update this to use some struct instead of errorName.
TypeMarker<? extends T> container = Optional.ofNullable(
errorNameToTypeMap.get(node.get("errorName").asText()))
.orElseThrow();
for (int i = 0; i < encodings.size(); i++) {
Encoding encoding = encodings.get(i);
if (encoding.supportsContentType(contentType.get())) {
return encoding.deserializer(container)
.deserialize(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
}
}
} else {
SerializableError serializableError = MAPPER.readValue(body, SerializableError.class);
throw new RemoteException(serializableError, code);
JsonNode errorNameNode = node.get("errorName");
if (errorNameNode == null) {
throwSerializableError(body, code);
}
Optional<Deserializer<? extends T>> maybeDeserializer =
Optional.ofNullable(errorNameToJsonDeserializerMap.get(errorNameNode.asText()));
if (maybeDeserializer.isEmpty()) {
throwSerializableError(body, code);
}
return maybeDeserializer
.get()
.deserialize(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
} catch (RemoteException remoteException) {
// rethrow the created remote exception
throw remoteException;
} catch (Exception e) {
throw new UnknownRemoteException(code, body);
}
@@ -156,17 +171,22 @@ private T decodeInternal(Response response) {
throw new UnknownRemoteException(code, body);
}

private static String toString(InputStream body) throws IOException {
private static void throwSerializableError(String body, int code) throws IOException {
SerializableError serializableError = MAPPER.readValue(body, SerializableError.class);
throw new RemoteException(serializableError, code);
}

static String toString(InputStream body) throws IOException {
try (Reader reader = new InputStreamReader(body, StandardCharsets.UTF_8)) {
return CharStreams.toString(reader);
}
}

private static ResponseDiagnostic diagnostic(Response response) {
static ResponseDiagnostic diagnostic(Response response) {
return new ResponseDiagnostic(diagnosticArgs(response));
}

private static ImmutableList<Arg<?>> diagnosticArgs(Response response) {
static ImmutableList<Arg<?>> diagnosticArgs(Response response) {
ImmutableList.Builder<Arg<?>> args = ImmutableList.<Arg<?>>builder().add(SafeArg.of("status", response.code()));
recordHeader(HttpHeaders.SERVER, response, args);
recordHeader(HttpHeaders.CONTENT_TYPE, response, args);
Original file line number Diff line number Diff line change
@@ -17,35 +17,16 @@
package com.palantir.conjure.java.dialogue.serde;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
import com.google.common.net.HttpHeaders;
import com.google.common.primitives.Longs;
import com.palantir.conjure.java.api.errors.QosException;
import com.palantir.conjure.java.api.errors.QosReason;
import com.palantir.conjure.java.api.errors.QosReasons;
import com.palantir.conjure.java.api.errors.QosReasons.QosResponseDecodingAdapter;
import com.palantir.conjure.java.api.errors.RemoteException;
import com.palantir.conjure.java.api.errors.SerializableError;
import com.palantir.conjure.java.api.errors.UnknownRemoteException;
import com.palantir.conjure.java.serialization.ObjectMappers;
import com.palantir.dialogue.Response;
import com.palantir.logsafe.Arg;
import com.palantir.logsafe.SafeArg;
import com.palantir.logsafe.SafeLoggable;
import com.palantir.logsafe.UnsafeArg;
import com.palantir.logsafe.exceptions.SafeExceptions;
import com.palantir.logsafe.logger.SafeLogger;
import com.palantir.logsafe.logger.SafeLoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Collections;
import java.util.Optional;

/**
@@ -59,59 +40,35 @@ public enum ErrorDecoder {

private static final SafeLogger log = SafeLoggerFactory.get(ErrorDecoder.class);
private static final ObjectMapper MAPPER = ObjectMappers.newClientObjectMapper();
private static final EndpointErrorDecoder<?> ENDPOINT_ERROR_DECODER =
new EndpointErrorDecoder<>(Collections.emptyMap(), Optional.empty());

public boolean isError(Response response) {
return 300 <= response.code() && response.code() <= 599;
return ENDPOINT_ERROR_DECODER.isError(response);
}

public RuntimeException decode(Response response) {
if (log.isDebugEnabled()) {
log.debug("Received an error response", diagnosticArgs(response));
log.debug("Received an error response", EndpointErrorDecoder.diagnosticArgs(response));
}
RuntimeException result = decodeInternal(response);
result.addSuppressed(diagnostic(response));
result.addSuppressed(EndpointErrorDecoder.diagnostic(response));
return result;
}

private RuntimeException decodeInternal(Response response) {
// TODO(rfink): What about HTTP/101 switching protocols?
// TODO(rfink): What about HEAD requests?

int code = response.code();
switch (code) {
case 308:
Optional<String> location = response.getFirstHeader(HttpHeaders.LOCATION);
if (location.isPresent()) {
String locationHeader = location.get();
try {
UnknownRemoteException remoteException = new UnknownRemoteException(code, "");
remoteException.initCause(
QosException.retryOther(qosReason(response), new URL(locationHeader)));
return remoteException;
} catch (MalformedURLException e) {
log.error(
"Failed to parse location header for QosException.RetryOther",
UnsafeArg.of("locationHeader", locationHeader),
e);
}
} else {
log.error("Retrieved HTTP status code 308 without Location header, cannot perform "
+ "redirect. This appears to be a server-side protocol violation.");
}
break;
case 429:
return response.getFirstHeader(HttpHeaders.RETRY_AFTER)
.map(Longs::tryParse)
.map(Duration::ofSeconds)
.map(duration -> QosException.throttle(qosReason(response), duration))
.orElseGet(() -> QosException.throttle(qosReason(response)));
case 503:
return QosException.unavailable(qosReason(response));
Optional<RuntimeException> maybeQosException = ENDPOINT_ERROR_DECODER.checkCode(response);
if (maybeQosException.isPresent()) {
return maybeQosException.get();
}

int code = response.code();
String body;
try {
body = toString(response.body());
body = EndpointErrorDecoder.toString(response.body());
} catch (NullPointerException | IOException e) {
UnknownRemoteException exception = new UnknownRemoteException(code, "<unparseable>");
exception.initCause(e);
@@ -130,75 +87,4 @@ private RuntimeException decodeInternal(Response response) {

return new UnknownRemoteException(code, body);
}

private static String toString(InputStream body) throws IOException {
try (Reader reader = new InputStreamReader(body, StandardCharsets.UTF_8)) {
return CharStreams.toString(reader);
}
}

private static ResponseDiagnostic diagnostic(Response response) {
return new ResponseDiagnostic(diagnosticArgs(response));
}

private static ImmutableList<Arg<?>> diagnosticArgs(Response response) {
ImmutableList.Builder<Arg<?>> args = ImmutableList.<Arg<?>>builder().add(SafeArg.of("status", response.code()));
recordHeader(HttpHeaders.SERVER, response, args);
recordHeader(HttpHeaders.CONTENT_TYPE, response, args);
recordHeader(HttpHeaders.CONTENT_LENGTH, response, args);
recordHeader(HttpHeaders.CONNECTION, response, args);
recordHeader(HttpHeaders.DATE, response, args);
recordHeader("x-envoy-response-flags", response, args);
recordHeader("x-envoy-response-code-details", response, args);
recordHeader("Response-Flags", response, args);
recordHeader("Response-Code-Details", response, args);
return args.build();
}

private static void recordHeader(String header, Response response, ImmutableList.Builder<Arg<?>> args) {
response.getFirstHeader(header).ifPresent(server -> args.add(SafeArg.of(header, server)));
}

private static final class ResponseDiagnostic extends RuntimeException implements SafeLoggable {

private static final String SAFE_MESSAGE = "Response Diagnostic Information";

private final ImmutableList<Arg<?>> args;

ResponseDiagnostic(ImmutableList<Arg<?>> args) {
super(SafeExceptions.renderMessage(SAFE_MESSAGE, args.toArray(new Arg<?>[0])));
this.args = args;
}

@Override
public String getLogMessage() {
return SAFE_MESSAGE;
}

@Override
public List<Arg<?>> getArgs() {
return args;
}

@Override
@SuppressWarnings("UnsynchronizedOverridesSynchronized") // nop
public Throwable fillInStackTrace() {
// no-op: stack trace generation is expensive, this type exists
// to simply associate diagnostic information with a failure.
return this;
}
}

private static QosReason qosReason(Response response) {
return QosReasons.parseFromResponse(response, DialogueQosResponseDecodingAdapter.INSTANCE);
}

private enum DialogueQosResponseDecodingAdapter implements QosResponseDecodingAdapter<Response> {
INSTANCE;

@Override
public Optional<String> getFirstHeader(Response response, String headerName) {
return response.getFirstHeader(headerName);
}
}
}
Original file line number Diff line number Diff line change
@@ -34,7 +34,6 @@ public void testBinary() throws IOException {
TestResponse response = new TestResponse().code(200).contentType("application/octet-stream");
BodySerDe serializers = new ConjureBodySerDe(
ImmutableList.of(WeightedEncoding.of(new ConjureBodySerDeTest.StubEncoding("application/json"))),
ErrorDecoder.INSTANCE,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
InputStream deserialized = serializers.inputStreamDeserializer().deserialize(response);
@@ -58,7 +57,6 @@ public void testBinary_optional_present() throws IOException {
TestResponse response = new TestResponse().code(200).contentType("application/octet-stream");
BodySerDe serializers = new ConjureBodySerDe(
ImmutableList.of(WeightedEncoding.of(new ConjureBodySerDeTest.StubEncoding("application/json"))),
ErrorDecoder.INSTANCE,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
Optional<InputStream> maybe =
Original file line number Diff line number Diff line change
@@ -52,8 +52,6 @@ public class ConjureBodySerDeTest {
private static final TypeMarker<String> TYPE = new TypeMarker<String>() {};
private static final TypeMarker<Optional<String>> OPTIONAL_TYPE = new TypeMarker<Optional<String>>() {};

private ErrorDecoder errorDecoder = ErrorDecoder.INSTANCE;

@Test
public void testRequestContentType() throws IOException {

@@ -76,7 +74,6 @@ private ConjureBodySerDe conjureBodySerDe(String... contentTypes) {
Arrays.stream(contentTypes)
.map(c -> WeightedEncoding.of(new StubEncoding(c)))
.collect(ImmutableList.toImmutableList()),
errorDecoder,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
}
@@ -115,7 +112,6 @@ public void testAcceptBasedOnWeight() throws IOException {

BodySerDe serializers = new ConjureBodySerDe(
ImmutableList.of(WeightedEncoding.of(plain, .5), WeightedEncoding.of(json, 1)),
ErrorDecoder.INSTANCE,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
// first encoding is default
@@ -174,7 +170,6 @@ public void if_deserialize_throws_response_is_still_closed() {
TestResponse response = new TestResponse().code(200).contentType("application/json");
BodySerDe serializers = new ConjureBodySerDe(
ImmutableList.of(WeightedEncoding.of(BrokenEncoding.INSTANCE)),
ErrorDecoder.INSTANCE,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
assertThatThrownBy(() -> serializers.deserializer(TYPE).deserialize(response))
Original file line number Diff line number Diff line change
@@ -80,7 +80,6 @@ public final class DefaultClientsTest {
private Response response = new TestResponse();
private BodySerDe bodySerde = new ConjureBodySerDe(
DefaultConjureRuntime.DEFAULT_ENCODINGS,
ErrorDecoder.INSTANCE,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
private final SettableFuture<Response> responseFuture = SettableFuture.create();
Original file line number Diff line number Diff line change
@@ -48,7 +48,6 @@
@ExtendWith(MockitoExtension.class)
public class EndpointErrorsConjureBodySerDeTest {
private static final ObjectMapper MAPPER = ObjectMappers.newServerObjectMapper();
private ErrorDecoder errorDecoder = ErrorDecoder.INSTANCE;

@Generated("by conjure-java")
private sealed interface EndpointReturnBaseType permits StringReturn, ErrorForEndpoint {}
@@ -144,6 +143,7 @@ public void testDeserializeCustomErrors() throws IOException {
EndpointErrorsConjureBodySerDeTest.EndpointReturnBaseType value =
serializers.deserializer(deserializerArgs).deserialize(response);

assertThat(value).isInstanceOf(ErrorForEndpoint.class);
assertThat(value)
.extracting("errorCode", "errorName", "errorInstanceId", "args")
.containsExactly(
@@ -175,7 +175,6 @@ private ConjureBodySerDe conjureBodySerDe(String... contentTypes) {
Arrays.stream(contentTypes)
.map(c -> WeightedEncoding.of(new TypeReturningStubEncoding(c)))
.collect(ImmutableList.toImmutableList()),
errorDecoder,
Encodings.emptyContainerDeserializer(),
DefaultConjureRuntime.DEFAULT_SERDE_CACHE_SPEC);
}

0 comments on commit eb93b54

Please sign in to comment.