package com.apollographql.apollo.internal;
import com.apollographql.apollo.ApolloMutationCall;
import com.apollographql.apollo.ApolloQueryCall;
import com.apollographql.apollo.CustomTypeAdapter;
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.OperationName;
import com.apollographql.apollo.api.Query;
import com.apollographql.apollo.api.Response;
import com.apollographql.apollo.api.ResponseFieldMapper;
import com.apollographql.apollo.api.ScalarType;
import com.apollographql.apollo.cache.CacheHeaders;
import com.apollographql.apollo.cache.http.HttpCachePolicy;
import com.apollographql.apollo.cache.normalized.ApolloStore;
import com.apollographql.apollo.cache.normalized.CacheControl;
import com.apollographql.apollo.exception.ApolloCanceledException;
import com.apollographql.apollo.exception.ApolloException;
import com.apollographql.apollo.exception.ApolloHttpException;
import com.apollographql.apollo.exception.ApolloNetworkException;
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.interceptor.ApolloCacheInterceptor;
import com.apollographql.apollo.internal.interceptor.ApolloParseInterceptor;
import com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor;
import com.apollographql.apollo.internal.interceptor.RealApolloInterceptorChain;
import com.apollographql.apollo.internal.util.ApolloLogger;
import com.squareup.moshi.Moshi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import okhttp3.Call;
import okhttp3.HttpUrl;
import static com.apollographql.apollo.api.internal.Utils.checkNotNull;
@SuppressWarnings("WeakerAccess")
public final class RealApolloCall<T> implements ApolloQueryCall<T>, ApolloMutationCall<T> {
final Operation operation;
final HttpUrl serverUrl;
final Call.Factory httpCallFactory;
final HttpCache httpCache;
final HttpCachePolicy.Policy httpCachePolicy;
final Moshi moshi;
final ResponseFieldMapper responseFieldMapper;
final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
final ApolloStore apolloStore;
final CacheControl cacheControl;
final CacheHeaders cacheHeaders;
final ApolloInterceptorChain interceptorChain;
final ExecutorService dispatcher;
final ApolloLogger logger;
final List<ApolloInterceptor> applicationInterceptors;
final List<OperationName> refetchQueryNames;
final AtomicBoolean executed = new AtomicBoolean();
volatile boolean canceled;
public static <T> Builder<T> builder() {
return new Builder<>();
}
private RealApolloCall(Builder<T> builder) {
this.operation = builder.operation;
this.serverUrl = builder.serverUrl;
this.httpCallFactory = builder.httpCallFactory;
this.httpCache = builder.httpCache;
this.httpCachePolicy = builder.httpCachePolicy;
this.moshi = builder.moshi;
this.responseFieldMapper = builder.responseFieldMapper;
this.customTypeAdapters = builder.customTypeAdapters;
this.apolloStore = builder.apolloStore;
this.cacheControl = builder.cacheControl;
this.cacheHeaders = builder.cacheHeaders;
this.dispatcher = builder.dispatcher;
this.logger = builder.logger;
this.applicationInterceptors = builder.applicationInterceptors;
this.refetchQueryNames = builder.refetchQueryNames;
interceptorChain = prepareInterceptorChain(operation);
}
@SuppressWarnings("unchecked") @Nonnull @Override public Response<T> execute() throws ApolloException {
if (!executed.compareAndSet(false, true)) {
throw new IllegalStateException("Already Executed");
}
if (canceled) {
throw new ApolloCanceledException("Canceled");
}
Response<T> response;
try {
response = interceptorChain.proceed().parsedResponse.or(new Response(operation));
} catch (Exception e) {
if (canceled) {
throw new ApolloCanceledException("Canceled", e);
} else {
throw e;
}
}
if (canceled) {
throw new ApolloCanceledException("Canceled");
}
return response;
}
@Override public void enqueue(@Nullable final Callback<T> callback) {
if (!executed.compareAndSet(false, true)) {
throw new IllegalStateException("Already Executed");
}
interceptorChain.proceedAsync(dispatcher, new ApolloInterceptor.CallBack() {
@Override public void onResponse(@Nonnull ApolloInterceptor.InterceptorResponse response) {
if (callback == null) {
return;
}
if (canceled) {
callback.onCanceledError(new ApolloCanceledException("Canceled"));
} else {
//noinspection unchecked
callback.onResponse(response.parsedResponse.get());
}
}
@Override public void onFailure(@Nonnull ApolloException e) {
if (callback == null) {
return;
}
if (canceled) {
callback.onCanceledError(new ApolloCanceledException("Canceled", e));
} else if (e instanceof ApolloHttpException) {
callback.onHttpError((ApolloHttpException) e);
} else if (e instanceof ApolloParseException) {
callback.onParseError((ApolloParseException) e);
} else if (e instanceof ApolloNetworkException) {
callback.onNetworkError((ApolloNetworkException) e);
} else {
callback.onFailure(e);
}
}
});
}
@Nonnull @Override public RealApolloQueryWatcher<T> watcher() {
return new RealApolloQueryWatcher<>(clone(), apolloStore);
}
@Nonnull @Override public RealApolloCall<T> httpCachePolicy(@Nonnull HttpCachePolicy.Policy httpCachePolicy) {
if (executed.get()) throw new IllegalStateException("Already Executed");
return toBuilder()
.httpCachePolicy(checkNotNull(httpCachePolicy, "httpCachePolicy == null"))
.build();
}
@Nonnull @Override public RealApolloCall<T> cacheControl(@Nonnull CacheControl cacheControl) {
if (executed.get()) throw new IllegalStateException("Already Executed");
return toBuilder()
.cacheControl(checkNotNull(cacheControl, "cacheControl == null"))
.build();
}
@Nonnull @Override public RealApolloCall<T> cacheHeaders(@Nonnull CacheHeaders cacheHeaders) {
if (executed.get()) throw new IllegalStateException("Already Executed");
return toBuilder()
.cacheHeaders(checkNotNull(cacheHeaders, "cacheHeaders == null"))
.build();
}
@Override public void cancel() {
canceled = true;
interceptorChain.dispose();
}
@Override public boolean isCanceled() {
return canceled;
}
@Override @Nonnull public RealApolloCall<T> clone() {
return toBuilder().build();
}
@Nonnull @Override public ApolloMutationCall<T> refetchQueries(@Nonnull OperationName... operationNames) {
if (executed.get()) throw new IllegalStateException("Already Executed");
return toBuilder()
.refetchQueryNames(Arrays.asList(checkNotNull(operationNames, "operationNames == null")))
.build();
}
public Builder<T> toBuilder() {
return RealApolloCall.<T>builder()
.operation(operation)
.serverUrl(serverUrl)
.httpCallFactory(httpCallFactory)
.httpCache(httpCache)
.httpCachePolicy(httpCachePolicy)
.moshi(moshi)
.responseFieldMapper(responseFieldMapper)
.customTypeAdapters(customTypeAdapters)
.apolloStore(apolloStore)
.cacheControl(cacheControl)
.cacheHeaders(cacheHeaders)
.dispatcher(dispatcher)
.logger(logger)
.applicationInterceptors(applicationInterceptors)
.refetchQueryNames(refetchQueryNames);
}
private ApolloInterceptorChain prepareInterceptorChain(Operation operation) {
List<ApolloInterceptor> interceptors = new ArrayList<>();
HttpCachePolicy.Policy httpCachePolicy = operation instanceof Query ? this.httpCachePolicy : null;
interceptors.addAll(applicationInterceptors);
interceptors.add(new ApolloCacheInterceptor(apolloStore, cacheControl, cacheHeaders, responseFieldMapper,
customTypeAdapters, dispatcher, logger));
interceptors.add(new ApolloParseInterceptor(httpCache, apolloStore.networkResponseNormalizer(), responseFieldMapper,
customTypeAdapters, logger));
interceptors.add(new ApolloServerInterceptor(serverUrl, httpCallFactory, httpCachePolicy, false, moshi, logger));
return new RealApolloInterceptorChain(operation, interceptors);
}
public static final class Builder<T> {
Operation operation;
HttpUrl serverUrl;
Call.Factory httpCallFactory;
HttpCache httpCache;
HttpCachePolicy.Policy httpCachePolicy;
Moshi moshi;
ResponseFieldMapper responseFieldMapper;
Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
ApolloStore apolloStore;
CacheControl cacheControl;
CacheHeaders cacheHeaders;
ApolloInterceptorChain interceptorChain;
ExecutorService dispatcher;
ApolloLogger logger;
List<ApolloInterceptor> applicationInterceptors;
List<OperationName> refetchQueryNames;
public Builder<T> operation(Operation operation) {
this.operation = operation;
return this;
}
public Builder<T> serverUrl(HttpUrl serverUrl) {
this.serverUrl = serverUrl;
return this;
}
public Builder<T> httpCallFactory(Call.Factory httpCallFactory) {
this.httpCallFactory = httpCallFactory;
return this;
}
public Builder<T> httpCache(HttpCache httpCache) {
this.httpCache = httpCache;
return this;
}
public Builder<T> httpCachePolicy(HttpCachePolicy.Policy httpCachePolicy) {
this.httpCachePolicy = httpCachePolicy;
return this;
}
public Builder<T> moshi(Moshi moshi) {
this.moshi = moshi;
return this;
}
public Builder<T> responseFieldMapper(ResponseFieldMapper responseFieldMapper) {
this.responseFieldMapper = responseFieldMapper;
return this;
}
public Builder<T> customTypeAdapters(Map<ScalarType, CustomTypeAdapter> customTypeAdapters) {
this.customTypeAdapters = customTypeAdapters;
return this;
}
public Builder<T> apolloStore(ApolloStore apolloStore) {
this.apolloStore = apolloStore;
return this;
}
public Builder<T> cacheControl(CacheControl cacheControl) {
this.cacheControl = cacheControl;
return this;
}
public Builder<T> cacheHeaders(CacheHeaders cacheHeaders) {
this.cacheHeaders = cacheHeaders;
return this;
}
public Builder<T> interceptorChain(ApolloInterceptorChain interceptorChain) {
this.interceptorChain = interceptorChain;
return this;
}
public Builder<T> dispatcher(ExecutorService dispatcher) {
this.dispatcher = dispatcher;
return this;
}
public Builder<T> logger(ApolloLogger logger) {
this.logger = logger;
return this;
}
public Builder<T> applicationInterceptors(List<ApolloInterceptor> applicationInterceptors) {
this.applicationInterceptors = applicationInterceptors;
return this;
}
public Builder<T> refetchQueryNames(List<OperationName> refetchQueryNames) {
this.refetchQueryNames = refetchQueryNames;
return this;
}
Builder() {
}
public RealApolloCall<T> build() {
return new RealApolloCall<>(this);
}
}
}