All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.apollographql.apollo.internal.RealAppSyncPrefetch Maven / Gradle / Ivy

/**
 * Copyright 2018-2019 Amazon.com,
 * Inc. or its affiliates. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

package com.apollographql.apollo.internal;

import com.amazonaws.mobileconnectors.appsync.AppSyncPrefetch;
import com.apollographql.apollo.interceptor.ApolloInterceptor;
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.cache.http.HttpCachePolicy;
import com.apollographql.apollo.api.internal.Optional;
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.interceptor.ApolloInterceptorChain;
import com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor;
import com.apollographql.apollo.internal.interceptor.RealApolloInterceptorChain;
import com.apollographql.apollo.internal.response.ScalarTypeAdapters;

import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.Response;

import static com.apollographql.apollo.internal.CallState.ACTIVE;
import static com.apollographql.apollo.internal.CallState.CANCELED;
import static com.apollographql.apollo.internal.CallState.IDLE;
import static com.apollographql.apollo.internal.CallState.TERMINATED;

@SuppressWarnings("WeakerAccess") public final class RealAppSyncPrefetch implements AppSyncPrefetch {
  final Operation operation;
  final HttpUrl serverUrl;
  final Call.Factory httpCallFactory;
  final ScalarTypeAdapters scalarTypeAdapters;
  final Executor dispatcher;
  final ApolloLogger logger;
  final ApolloCallTracker tracker;
  final ApolloInterceptorChain interceptorChain;
  final boolean sendOperationIds;
  final AtomicReference state = new AtomicReference<>(IDLE);
  final AtomicReference originalCallback = new AtomicReference<>();

  public RealAppSyncPrefetch(Operation operation, HttpUrl serverUrl, Call.Factory httpCallFactory,
                             ScalarTypeAdapters scalarTypeAdapters, Executor dispatcher, ApolloLogger logger, ApolloCallTracker callTracker,
                             boolean sendOperationIds) {
    this.operation = operation;
    this.serverUrl = serverUrl;
    this.httpCallFactory = httpCallFactory;
    this.scalarTypeAdapters = scalarTypeAdapters;
    this.dispatcher = dispatcher;
    this.logger = logger;
    this.tracker = callTracker;
    this.sendOperationIds = sendOperationIds;
    interceptorChain = new RealApolloInterceptorChain(Collections.singletonList(
        new ApolloServerInterceptor(serverUrl, httpCallFactory, HttpCachePolicy.NETWORK_ONLY, true,
            scalarTypeAdapters, logger, sendOperationIds)
    ));
  }

  @Override public void enqueue(@Nullable final Callback responseCallback) {
    try {
      activate(Optional.fromNullable(responseCallback));
    } catch (ApolloCanceledException e) {
      if (responseCallback != null) {
        responseCallback.onFailure(e);
      } else {
        logger.e(e, "Operation: %s was canceled", operation().name().name());
      }
      return;
    }

    ApolloInterceptor.InterceptorRequest request = ApolloInterceptor.InterceptorRequest.builder(operation).build();
    interceptorChain.proceedAsync(request, dispatcher, interceptorCallbackProxy());
  }

  @Nonnull @Override public Operation operation() {
    return operation;
  }

  private ApolloInterceptor.CallBack interceptorCallbackProxy() {
    return new ApolloInterceptor.CallBack() {
      @Override public void onResponse(@Nonnull ApolloInterceptor.InterceptorResponse response) {
        Response httpResponse = response.httpResponse.get();
        try {
          Optional callback = terminate();
          if (!callback.isPresent()) {
            logger.d("onResponse for prefetch operation: %s. No callback present.", operation().name().name());
            return;
          }
          if (httpResponse.isSuccessful()) {
            callback.get().onSuccess();
          } else {
            callback.get().onHttpError(new ApolloHttpException(httpResponse));
          }
        } finally {
          httpResponse.close();
        }
      }

      @Override public void onFailure(@Nonnull ApolloException e) {
        Optional callback = terminate();
        if (!callback.isPresent()) {
          logger.e(e, "onFailure for prefetch operation: %s. No callback present.", operation().name().name());
          return;
        }
        if (e instanceof ApolloHttpException) {
          callback.get().onHttpError((ApolloHttpException) e);
        } else if (e instanceof ApolloNetworkException) {
          callback.get().onNetworkError((ApolloNetworkException) e);
        } else {
          callback.get().onFailure(e);
        }

      }

      @Override public void onCompleted() {
        // Prefetch is only called with NETWORK_ONLY, so callback api does not need onComplete as it is the same as
        // onResponse.
      }

      @Override public void onFetch(ApolloInterceptor.FetchSourceType sourceType) {
      }
    };
  }

  @Override public AppSyncPrefetch clone() {
    return new RealAppSyncPrefetch(operation, serverUrl, httpCallFactory, scalarTypeAdapters, dispatcher,
        logger, tracker, sendOperationIds);
  }

  @Override public synchronized void cancel() {
    switch (state.get()) {
      case ACTIVE:
        try {
          interceptorChain.dispose();
        } finally {
          tracker.unregisterPrefetchCall(this);
          originalCallback.set(null);
          state.set(CANCELED);
        }
        break;
      case IDLE:
        state.set(CANCELED);
        break;
      case CANCELED:
      case TERMINATED:
        // These are not illegal states, but cancelling does nothing
        break;
      default:
        throw new IllegalStateException("Unknown state");
    }
  }

  @Override public boolean isCanceled() {
    return state.get() == CANCELED;
  }

  private synchronized void activate(Optional callback) throws ApolloCanceledException {
    switch (state.get()) {
      case IDLE:
        originalCallback.set(callback.orNull());
        tracker.registerPrefetchCall(this);
        break;
      case CANCELED:
        throw new ApolloCanceledException("Call is cancelled.");
      case TERMINATED:
      case ACTIVE:
        throw new IllegalStateException("Already Executed");
      default:
        throw new IllegalStateException("Unknown state");
    }
    state.set(ACTIVE);
  }

  private synchronized Optional terminate() {
    switch (state.get()) {
      case ACTIVE:
        tracker.unregisterPrefetchCall(this);
        state.set(TERMINATED);
        return Optional.fromNullable(originalCallback.getAndSet(null));
      case CANCELED:
        return Optional.fromNullable(originalCallback.getAndSet(null));
      case IDLE:
      case TERMINATED:
        throw new IllegalStateException(
            CallState.IllegalStateMessage.forCurrentState(state.get()).expected(ACTIVE, CANCELED));
      default:
        throw new IllegalStateException("Unknown state");
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy