Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 216/move converter #218

Merged
merged 22 commits into from
Feb 20, 2017

Conversation

sav007
Copy link
Contributor

@sav007 sav007 commented Feb 19, 2017

Closes #216

First version of ApolloClient.

Api:
<T extends Operation> ApolloCall newCall(@Nonnull T operation)

public interface ApolloCall {

  void cancel();

  <T extends Operation.Data> Response<T> execute() throws IOException;

  <T extends Operation.Data> ApolloCall enqueue(@Nullable Callback<T> callback);

  ApolloCall clone();

  public interface Callback<T extends Operation.Data> {
    void onResponse(Response<T> response);

    void onFailure(Exception e);
  }
}

Usage (will be scheduled on okhttp3.Dispatcher#executorService):

apolloClient.newCall(new AllPlanets()).enqueue(new ApolloCall.Callback<AllPlanets.Data>() {
      @Override public void onResponse(Response<AllPlanets.Data> response) {
      ...
      }

      @Override public void onFailure(Exception e) {
      ...
      }
    });

Or (sync call)

Response<AllPlanets.Data> body = apolloClient.newCall(new AllPlanets()).execute();

return this;
}

public <T> Builder withResponseFieldMapper(@Nonnull Class type,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be removed in next PR, as we drop support of interfaces

return this;
}

public Builder withResponseFieldMappers(@Nonnull Map<Type, ResponseFieldMapper> mappers) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be removed in next PR, as we drop support of interfaces


Call httpCall = callFactory.newCall(request);
if (callRef.compareAndSet(null, httpCall)) {
httpCall.enqueue(new okhttp3.Callback() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it will be scheduled to be executed on okhttp3.Dispatcher that has own ExecutorService

.withCustomTypeAdapter(CustomType.DATETIME, dateCustomTypeAdapter)
.build())
.okHttpClient(new OkHttpClient.Builder().build())
.withResponseFieldMapper(AllPlanets.class, new AllPlanets.Data.Mapper(AllPlanets.Data.FACTORY))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with next PR these mapper registrations won't be needed at all.

import static com.apollographql.android.Utils.checkNotNull;

/** TODO */
public final class ApolloClient {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should implement something like

interface Factory {
    ApolloCall newCall(Operation operation);
  }

This would keep client stateless. Instead ApolloCall is an interface similar to Okhttp3.Call which can actually do exection

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to make it implement Factory, now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need but would be nice to have interface that defines that ApolloClient is a factory that creates ApolloCall. In case we ever want to migrate to another Client that can also produce ApolooCall

import okhttp3.RequestBody;
import okio.Buffer;

final class HttpOperationRequest<T extends Operation.Data> implements OperationRequest<T> {
Copy link
Contributor

@digitalbuddha digitalbuddha Feb 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move all this to a RealApolloCall which implements ApolloCall, you can model that after okhttp3.RealCall which imlpements okhttp3.Call below

public interface Call extends Cloneable {
  /** Returns the original request that initiated this call. */
  Request request();

  /**
   * Invokes the request immediately, and blocks until the response can be processed or is in
   * error.
   *
   * <p>The caller may read the response body with the response's {@link Response#body} method. To
   * avoid leaking resources callers must {@linkplain ResponseBody close the response body}.
   *
   * <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
   * not necessarily indicate application-layer success: {@code response} may still indicate an
   * unhappy HTTP response code like 404 or 500.
   *
   * @throws IOException if the request could not be executed due to cancellation, a connectivity
   * problem or timeout. Because networks can fail during an exchange, it is possible that the
   * remote server accepted the request before the failure.
   * @throws IllegalStateException when the call has already been executed.
   */
  Response execute() throws IOException;

  /**
   * Schedules the request to be executed at some point in the future.
   *
   * <p>The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
   * immediately unless there are several other requests currently being executed.
   *
   * <p>This client will later call back {@code responseCallback} with either an HTTP response or a
   * failure exception.
   *
   * @throws IllegalStateException when the call has already been executed.
   */
  void enqueue(Callback responseCallback);

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  void cancel();

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
   */
  boolean isExecuted();

  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}


import javax.annotation.Nullable;

public interface OperationRequest<T extends Operation.Data> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApolloCall


private Response<T> parse(okhttp3.Response response) throws IOException {
int code = response.code();
if (code < 200 || code >= 300) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

purposefully excluding 300 and up...my bad...

Copy link
Contributor

@digitalbuddha digitalbuddha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it :-)

@@ -18,6 +21,10 @@ public boolean isSuccessful() {
return errors == null || errors.isEmpty();
}

@Nonnull public Operation getOperation() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick - if there is no setOperation I don't think the get prefix is necessary

.build();

Call call = callFactory.newCall(request);
if (callRef.compareAndSet(null, call)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary with implementation of call.execute being

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we need this as RealApolloCall keeps ref on okhttp3.Call in order to cancel request.

@@ -42,7 +42,7 @@ class ApolloPlugin implements Plugin<Project> {
project.getGradle().addListener(new DependencyResolutionListener() {
@Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
compileDepSet.add(project.dependencies.create("com.apollographql.android:api:${VersionKt.VERSION}"))
compileDepSet.add(project.dependencies.create("com.apollographql.android:api:0.2.1"))
Copy link
Contributor Author

@sav007 sav007 Feb 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make plugin test happy, as now they are failing due to missing new snapshot verion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok but can't merge this way or it will make snapshot like this

}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')

tasks.withType(Checkstyle) {
exclude '**/BufferedSourceJsonReader.java'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why excluded?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because checkstyle will fail and BufferedSourceJsonReader was copied from retrofit as is without modification (we need it for ResponseReader)


void cancel();

<T extends Operation.Data> Response<T> execute() throws IOException;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nullability annotation on return value


<T extends Operation.Data> ApolloCall enqueue(@Nullable Callback<T> callback);

ApolloCall clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApolloCall clone();

public interface Callback<T extends Operation.Data> {
void onResponse(Response<T> response);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullability on both methods

import javax.annotation.Nonnull;

public interface ApolloCallFactory {
<T extends Operation> ApolloCall newCall(@Nonnull T operation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullability

}
}

@Override public <T extends Operation.Data> Response<T> execute() throws IOException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullability (I'm really calling out only public api methods)

return parseHttpResponse(httpCall.execute());
}

@Override public <T extends Operation.Data> ApolloCall enqueue(final Callback<T> callback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@digitalbuddha
Copy link
Contributor

👍 @brianPlummer mind taking a 1 over again?

build.gradle Outdated
@@ -31,7 +29,8 @@ ext {
spock: 'org.spockframework:spock-core:0.7-groovy-2.0',
truth: 'com.google.truth:truth:0.30',
mockWebServer: 'com.squareup.okhttp3:mockwebserver:3.5.0',
okhttpLoggingInterceptor: 'com.squareup.okhttp3:logging-interceptor:3.5.0'
okhttpLoggingInterceptor: 'com.squareup.okhttp3:logging-interceptor:3.5.0',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be 3.6.0 as well....feel free to leave it if you want...i can make this an okhttp version var

@brianPlummer
Copy link
Contributor

+1 looks great! good work! :)

@sav007 sav007 merged commit 9b53ac8 into apollographql:master Feb 20, 2017
@sav007 sav007 deleted the feature-216/move-converter branch February 20, 2017 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants