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

clarifai2.api.request.ClarifaiRequest Maven / Gradle / Ivy

The newest version!
package clarifai2.api.request;

import clarifai2.internal.grpc.api.V2Grpc;
import clarifai2.api.BaseClarifaiClient;
import clarifai2.api.ClarifaiClient;
import clarifai2.api.ClarifaiResponse;
import clarifai2.dto.ClarifaiStatus;
import clarifai2.exception.ClarifaiException;
import clarifai2.exception.ClarifaiClientClosedException;
import clarifai2.exception.NetworkException;
import clarifai2.grpc.JsonChannel;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gson.JsonElement;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.util.JsonFormat;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import static clarifai2.internal.InternalUtil.MEDIA_TYPE_JSON;
import static clarifai2.internal.InternalUtil.fromJson;

/**
 * An interface returned by the {@link ClarifaiClient} used to execute an API request.
 *
 * @param  the data-type returned by a successful API call
 */
public interface ClarifaiRequest {
  /**
   * Blocks this thread until a response is received, successful or not.
   *
   * @return the response retrieved by invoking this request
   * @throws ClarifaiException if an error occurs while executing
   */
  @NotNull ClarifaiResponse executeSync();

  /**
   * Executes the given request in a background process, and then returns the result to this one
   * via the callback.
   *
   * @param callback the object that will be notified with the results create the request, successful or not.
   */
  void executeAsync(@Nullable Callback callback);

  /**
   * Executes the given request in a background process, and then returns the result to this one via the onSuccess
   * callback. If an API failure or a network error occurs, a {@link ClarifaiException} will be thrown.
   *
   * @param onSuccess the callback to be notified with the results of the API call if it was successful
   * @throws ClarifaiException if the request is not successful
   */
  void executeAsync(@Nullable OnSuccess onSuccess) throws ClarifaiException;

  /**
   * Executes the given request in a background process, and then returns the result to this one via the onSuccess
   * callback if it was successful, or via the onFailure callback if it was unsuccessful. If a network error occurs,
   * a {@link ClarifaiException} will be thrown.
   *
   * @param onSuccess the callback to be notified with the results of the API call if it was successful
   * @param onFailure the callback to be notified with the results of the API call if it was not successful
   * @throws ClarifaiException if the request is not successful due to a network error
   */
  void executeAsync(@Nullable OnSuccess onSuccess, @Nullable OnFailure onFailure);

  /**
   * Executes the given request in a background process, and then returns the result to this one via onSuccess,
   * onFailure, or onNetworkError based on the response.
   *
   * @param onSuccess      the callback that is notified if the API call was successful
   * @param onFailure      the callback that is notified if the API call is not successful
   * @param onNetworkError the callback that is notified if there is a network error while making this API call
   */
  void executeAsync(
      @Nullable OnSuccess onSuccess,
      @Nullable OnFailure onFailure,
      @Nullable OnNetworkError onNetworkError
  );

  interface OnSuccess {
    void onClarifaiResponseSuccess(@NotNull RESULT result);
  }


  interface OnFailure {
    void onClarifaiResponseUnsuccessful(int errorCode);
  }


  interface OnNetworkError {
    void onClarifaiResponseNetworkError(@NotNull IOException e);
  }


  /**
   * One of the three methods in this callback is executed when
   * {@link ClarifaiRequest#executeAsync(Callback)} is called on a {@link ClarifaiRequest}
   *
   * @param  The type create data that will be returned if this API call was successful
   */
  interface Callback {
    /**
     * Invoked when the API call was successful.
     *
     * @param result The result create the API call, which was successful.
     */
    void onClarifaiResponseSuccess(@NotNull RESULT result);

    /**
     * Invoked when the API call reached the server, but a 4xx/5xx error was returned.
     *
     * @param errorCode The (unsuccessful) error code
     */
    void onClarifaiResponseUnsuccessful(int errorCode);

    /**
     * Invoked when the request did not successfully reach the server
     *
     * @param e The IOException associated with this connectivity error
     */
    void onClarifaiResponseNetworkError(@NotNull IOException e);
  }


  /**
   * A request and the deserialization that goes along with it, if the request was successful
   *
   * @param  the type to deserialize to
   */
  interface DeserializedRequest {
    @NotNull ListenableFuture httpRequestGrpc();
    @NotNull T unmarshalerGrpc(Object returnedObject);
  }


  abstract class Adapter implements ClarifaiRequest {
    @NotNull protected final BaseClarifaiClient client;

    protected Adapter(@NotNull BaseClarifaiClient client) {
      this.client = client;
    }

    @Override public final void executeAsync(@Nullable OnSuccess onSuccess) throws ClarifaiException {
      executeAsync(onSuccess, null);
    }

    @Override
    public final void executeAsync(@Nullable OnSuccess onSuccess,
        @Nullable OnFailure onFailure) {
      executeAsync(onSuccess, onFailure, null);
    }

    @Override
    public final void executeAsync(@Nullable final OnSuccess onSuccess,
        @Nullable final OnFailure onFailure,
        @Nullable final OnNetworkError onNetworkError) {
      executeAsync(new Callback() {
        @Override public void onClarifaiResponseSuccess(@NotNull T t) {
          if (onSuccess != null) {
            onSuccess.onClarifaiResponseSuccess(t);
          }
        }

        @Override public void onClarifaiResponseUnsuccessful(int errorCode) {
          if (onFailure == null) {
            throw new ClarifaiException("API failure occurred and was not handled. HTTP code: " + errorCode);
          }
          onFailure.onClarifaiResponseUnsuccessful(errorCode);
        }

        @Override public void onClarifaiResponseNetworkError(@NotNull IOException e) {
          if (onNetworkError == null) {
            throw new ClarifaiException("Network error occurred while making an API call. Error: " + e.getMessage());
          }
          onNetworkError.onClarifaiResponseNetworkError(e);
        }
      });
    }
  }


  abstract class Builder extends Adapter {

    @Nullable private final MediaType requestContentType;

    protected Builder(@NotNull BaseClarifaiClient client) {
      this(client, MEDIA_TYPE_JSON);
    }

    protected Builder(@NotNull BaseClarifaiClient client, @Nullable MediaType contentType) {
      super(client);
      requestContentType = contentType;
    }

    @NotNull public V2Grpc.V2FutureStub stub() {
      return V2Grpc.newFutureStub(new JsonChannel(client.clarifaiHttpClient))
          .withOption(JsonChannel.CLARIFAI_METHOD_OPTION, method())
          .withOption(JsonChannel.CLARIFAI_BASE_URL_OPTION, client.baseURL.toString())
          .withOption(JsonChannel.CLARIFAI_SUB_URL_OPTION, subUrl());
    }

    @NotNull @Override public final ClarifaiResponse executeSync() {
      return build().executeSync();
    }

    @Override public final void executeAsync(@Nullable Callback callback) {
      build().executeAsync(callback);
    }

    @NotNull protected ClarifaiRequest build() {
      return new Impl<>(client, request());
    }

    @NotNull protected abstract String method();
    @NotNull protected abstract String subUrl();

    @NotNull protected abstract DeserializedRequest request();

    @NotNull protected final Request getRequest(@NotNull String endpoint) {
      return builder(endpoint).get().build();
    }

    @NotNull protected final Request deleteRequest(@NotNull String endpoint, @NotNull JsonElement deleteBody) {
      return builder(endpoint).delete(toRequestBody(deleteBody)).build();
    }

    @NotNull protected final Request postRequest(@NotNull String endpoint, @NotNull JsonElement postBody) {
      return builder(endpoint).post(toRequestBody(postBody)).build();
    }

    @NotNull protected final Request patchRequest(@NotNull String endpoint, @NotNull JsonElement patchBody) {
      return builder(endpoint).patch(toRequestBody(patchBody)).build();
    }

    @NotNull private Request.Builder builder(@NotNull String endpoint) {
      return new Request.Builder().url(toHTTPUrl(endpoint));
    }

    @NotNull private HttpUrl toHTTPUrl(@NotNull String endpoint) {
      if (endpoint.charAt(0) == '/') {
        endpoint = endpoint.substring(1);
      }
      return client.baseURL.newBuilder().addPathSegments(endpoint).build();
    }

    @NotNull private RequestBody toRequestBody(@NotNull JsonElement json) {
      return RequestBody.create(requestContentType, client.gson.toJson(json));
    }

  }


  class Impl extends Adapter {

    @NotNull private final DeserializedRequest request;

    Impl(
        @NotNull BaseClarifaiClient client,
        @NotNull DeserializedRequest request
    ) {
      super(client);
      this.request = request;
    }

    @SuppressWarnings("ConstantConditions")
    @NotNull
    @Override
    public ClarifaiResponse executeSync() {
      try {
        Object o;
        try {
          ListenableFuture listenableFuture = request.httpRequestGrpc();
          o = listenableFuture.get();
        } catch (IllegalArgumentException|ClarifaiClientClosedException e) {
          throw e;
        } catch (RuntimeException e) {
          throw new NetworkException(e);
        } catch (Exception e) {
          throw new NetworkException(e);
        }

        JsonFormat.Printer printer = JsonFormat.printer();
        String rawJSON = printer.print((MessageOrBuilder) o);
        // TODO(Rok) HIGH: Code is not always 200.
        int code = 200;

        Object statusObj;
        try {
          Method getStatus = o.getClass().getMethod("getStatus");
          statusObj = getStatus.invoke(o);
        } catch (Exception e) {
          return new ClarifaiResponse.Failure<>(ClarifaiStatus.unknown(), code, rawJSON);
        }
        final ClarifaiStatus status = ClarifaiStatus.deserialize(statusObj);

        final boolean successfulHTTPCode = 200 <= code && code < 300;
        if (successfulHTTPCode && (status == null || status.equals(ClarifaiStatus.success()))) {
          return new ClarifaiResponse.Successful<>(
              status,
              code,
              rawJSON,
              request.unmarshalerGrpc(o)
          );
        } else if (successfulHTTPCode && status.equals(ClarifaiStatus.mixedSuccess())) {
          return new ClarifaiResponse.MixedSuccess<>(
              status,
              code,
              rawJSON,
              request.unmarshalerGrpc(o)
          );
        }
        return new ClarifaiResponse.Failure<>(status, code, rawJSON);
      } catch (ClarifaiClientClosedException e) {
        throw e;
      } catch (NetworkException|IOException e) {
        return new ClarifaiResponse.NetworkError<>(ClarifaiStatus.networkError(e));
      }
    }

    @Override public void executeAsync(@Nullable final Callback callback) {
      client.httpClient.dispatcher().executorService().submit(new Callable() {
        @Override public Void call() throws Exception {
          final ClarifaiResponse response = executeSync();
          if (callback != null) {
            if (response.isSuccessful()) {
              callback.onClarifaiResponseSuccess(response.get());
            } else {
              if (response.getStatus().networkErrorOccurred()) {
                callback.onClarifaiResponseNetworkError(new IOException(response.getStatus().errorDetails()));
              } else {
                callback.onClarifaiResponseUnsuccessful(response.responseCode());
              }
            }
          }
          return null;
        }
      });
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy