package com.apollographql.apollo.internal.interceptor;
import com.apollographql.apollo.CustomTypeAdapter;
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.Response;
import com.apollographql.apollo.api.ResponseFieldMapper;
import com.apollographql.apollo.api.ScalarType;
import com.apollographql.apollo.exception.ApolloException;
import com.apollographql.apollo.exception.ApolloHttpException;
import com.apollographql.apollo.exception.ApolloParseException;
import com.apollographql.apollo.interceptor.ApolloInterceptor;
import com.apollographql.apollo.interceptor.ApolloInterceptorChain;
import com.apollographql.apollo.internal.cache.http.HttpCache;
import com.apollographql.apollo.internal.cache.normalized.ResponseNormalizer;
import com.apollographql.apollo.internal.util.ApolloLogger;
import java.io.Closeable;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import javax.annotation.Nonnull;
/**
* ApolloParseInterceptor is a concrete {@link ApolloInterceptor} responsible for inflating the http responses into
* models. To get the http responses, it hands over the control to the next interceptor in the chain and proceeds to
* then parse the returned response.
*/
public final class ApolloParseInterceptor implements ApolloInterceptor {
private final HttpCache httpCache;
private final ResponseNormalizer<Map<String, Object>> normalizer;
private final ResponseFieldMapper responseFieldMapper;
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
private final ApolloLogger logger;
public ApolloParseInterceptor(HttpCache httpCache, ResponseNormalizer<Map<String, Object>> normalizer,
ResponseFieldMapper responseFieldMapper, Map<ScalarType, CustomTypeAdapter> customTypeAdapters,
ApolloLogger logger) {
this.httpCache = httpCache;
this.normalizer = normalizer;
this.responseFieldMapper = responseFieldMapper;
this.customTypeAdapters = customTypeAdapters;
this.logger = logger;
}
@Override @Nonnull public InterceptorResponse intercept(Operation operation, ApolloInterceptorChain chain)
throws ApolloException {
InterceptorResponse response = chain.proceed();
return parse(operation, response.httpResponse.get());
}
@Override
public void interceptAsync(@Nonnull final Operation operation, @Nonnull ApolloInterceptorChain chain,
@Nonnull ExecutorService dispatcher, @Nonnull final CallBack callBack) {
chain.proceedAsync(dispatcher, new CallBack() {
@Override public void onResponse(@Nonnull InterceptorResponse response) {
try {
InterceptorResponse result = parse(operation, response.httpResponse.get());
callBack.onResponse(result);
} catch (ApolloException e) {
onFailure(e);
}
}
@Override public void onFailure(@Nonnull ApolloException e) {
callBack.onFailure(e);
}
});
}
@Override public void dispose() {
//no op
}
@SuppressWarnings("unchecked") private InterceptorResponse parse(Operation operation, okhttp3.Response httpResponse)
throws ApolloHttpException, ApolloParseException {
String cacheKey = httpResponse.request().header(HttpCache.CACHE_KEY_HEADER);
if (httpResponse.isSuccessful()) {
try {
HttpResponseBodyParser parser = new HttpResponseBodyParser(operation, responseFieldMapper, customTypeAdapters);
Response parsedResponse = parser.parse(httpResponse.body(), normalizer);
if (parsedResponse.hasErrors() && httpCache != null) {
httpCache.removeQuietly(cacheKey);
}
return new InterceptorResponse(httpResponse, parsedResponse, normalizer.records());
} catch (Exception rethrown) {
logger.e(rethrown, "Failed to parse network response for operation: %s", operation);
closeQuietly(httpResponse);
if (httpCache != null) {
httpCache.removeQuietly(cacheKey);
}
throw new ApolloParseException("Failed to parse http response", rethrown);
}
} else {
logger.e("Failed to parse network response: %s", httpResponse);
throw new ApolloHttpException(httpResponse);
}
}
private static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ignored) {
}
}
}
}