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

Introduce explicit re-fetch query for mutation operation. #504

Merged
merged 15 commits into from
May 27, 2017
18 changes: 18 additions & 0 deletions .idea/codeStyleSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apollo-integration/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies {
testCompile dep.mockWebServer
testCompile dep.okhttpTestSupport
testCompile dep.moshi
testCompile dep.cache
}

apollo {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mutation CreateReview($episode: Episode!, $review: ReviewInput!) {
createReview(episode: $episode, review: $review) {
stars
commentary
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query ReviewsByEpisode($episode: Episode!) {
reviews(episode: $episode) {
stars
commentary
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.apollographql.apollo;

import com.apollographql.android.impl.normalizer.CreateReview;
import com.apollographql.android.impl.normalizer.ReviewsByEpisode;
import com.apollographql.android.impl.normalizer.type.ColorInput;
import com.apollographql.android.impl.normalizer.type.Episode;
import com.apollographql.android.impl.normalizer.type.ReviewInput;
import com.apollographql.apollo.api.Response;
import com.apollographql.apollo.cache.normalized.CacheControl;
import com.apollographql.apollo.cache.normalized.lru.EvictionPolicy;
import com.apollographql.apollo.cache.normalized.lru.LruNormalizedCacheFactory;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;

import okhttp3.OkHttpClient;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;

import static com.google.common.truth.Truth.assertThat;

public class QueryRefetchTest {
private ApolloClient apolloClient;
private MockWebServer server;

@Before public void setUp() throws IOException {
server = new MockWebServer();
server.start();
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

apolloClient = ApolloClient.builder()
.serverUrl(server.url("/"))
.okHttpClient(okHttpClient)
.normalizedCache(new LruNormalizedCacheFactory(EvictionPolicy.NO_EVICTION), new IdFieldCacheKeyResolver())
.build();
}

@After public void tearDown() {
try {
server.shutdown();
} catch (IOException ignored) {
}
}

@Test public void refetch_query_no_pre_cached() throws Exception {
server.enqueue(mockResponse("CreateReviewResponse.json"));
CreateReview mutation = new CreateReview(
Episode.EMPIRE,
ReviewInput.builder().stars(5).commentary("Awesome").favoriteColor(ColorInput.builder().build()).build()
);

server.enqueue(mockResponse("ReviewsEmpireEpisodeResponse.json"));
ReviewsByEpisode empireReviewsQuery = new ReviewsByEpisode(Episode.EMPIRE);

server.enqueue(mockResponse("ReviewsJediEpisodeResponse.json"));
ReviewsByEpisode jediReviewsQuery = new ReviewsByEpisode(Episode.JEDI);

apolloClient.mutate(mutation).refetchQueries(empireReviewsQuery, jediReviewsQuery).execute();
assertThat(server.getRequestCount()).isEqualTo(3);

Response<ReviewsByEpisode.Data> empireReviewsQueryResponse = apolloClient.query(empireReviewsQuery).cacheControl
(CacheControl.CACHE_ONLY).execute();
assertThat(empireReviewsQueryResponse.data().reviews()).hasSize(3);
assertThat(empireReviewsQueryResponse.data().reviews().get(2).stars()).isEqualTo(5);
assertThat(empireReviewsQueryResponse.data().reviews().get(2).commentary()).isEqualTo("Amazing");

Response<ReviewsByEpisode.Data> jediReviewsQueryResponse = apolloClient.query(jediReviewsQuery).cacheControl
(CacheControl.CACHE_ONLY).execute();
assertThat(jediReviewsQueryResponse.data().reviews()).hasSize(1);
assertThat(jediReviewsQueryResponse.data().reviews().get(0).stars()).isEqualTo(5);
assertThat(jediReviewsQueryResponse.data().reviews().get(0).commentary()).isEqualTo("Fascinating");
}

@Test public void refetch_query_pre_cached() throws Exception {
server.enqueue(mockResponse("ReviewsEmpireEpisodeResponse.json"));
ReviewsByEpisode empireReviewsQuery = new ReviewsByEpisode(Episode.EMPIRE);
apolloClient.query(empireReviewsQuery).cacheControl(CacheControl.NETWORK_FIRST).execute();

Response<ReviewsByEpisode.Data> empireReviewsQueryResponse = apolloClient.query(empireReviewsQuery)
.cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(empireReviewsQueryResponse.data().reviews()).hasSize(3);
assertThat(empireReviewsQueryResponse.data().reviews().get(2).stars()).isEqualTo(5);
assertThat(empireReviewsQueryResponse.data().reviews().get(2).commentary()).isEqualTo("Amazing");

server.enqueue(mockResponse("CreateReviewResponse.json"));
CreateReview mutation = new CreateReview(
Episode.EMPIRE,
ReviewInput.builder().stars(5).commentary("Awesome").favoriteColor(ColorInput.builder().build()).build()
);

server.enqueue(mockResponse("ReviewsEmpireEpisodeResponseUpdated.json"));
apolloClient.mutate(mutation).refetchQueries(empireReviewsQuery).execute();
assertThat(server.getRequestCount()).isEqualTo(3);

empireReviewsQueryResponse = apolloClient.query(empireReviewsQuery).cacheControl
(CacheControl.CACHE_ONLY).execute();
assertThat(empireReviewsQueryResponse.data().reviews()).hasSize(4);
assertThat(empireReviewsQueryResponse.data().reviews().get(3).stars()).isEqualTo(5);
assertThat(empireReviewsQueryResponse.data().reviews().get(3).commentary()).isEqualTo("Awesome");
}

private MockResponse mockResponse(String fileName) throws IOException {
return new MockResponse().setChunkedBody(Utils.readFileToString(getClass(), "/" + fileName), 32);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"data": {
"createReview": {
"__typename": "Review",
"stars": 5,
"commentary": "Awesome"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"data": {
"reviews": [
{
"__typename": "Review",
"stars": 1,
"commentary": "Boring"
},
{
"__typename": "Review",
"stars": 2,
"commentary": "So-so"
},
{
"__typename": "Review",
"stars": 5,
"commentary": "Amazing"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"data": {
"reviews": [
{
"__typename": "Review",
"stars": 1,
"commentary": "Boring"
},
{
"__typename": "Review",
"stars": 2,
"commentary": "So so"
},
{
"__typename": "Review",
"stars": 5,
"commentary": "Amazing"
},
{
"__typename": "Review",
"stars": 5,
"commentary": "Awesome"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"data": {
"reviews": [
{
"__typename": "Review",
"stars": 5,
"commentary": "Fascinating"
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.OperationName;
import com.apollographql.apollo.api.Query;
import com.apollographql.apollo.api.ResponseFieldMapper;
import com.apollographql.apollo.api.ScalarType;
import com.apollographql.apollo.api.internal.Optional;
import com.apollographql.apollo.cache.CacheHeaders;
Expand All @@ -19,6 +18,7 @@
import com.apollographql.apollo.interceptor.ApolloInterceptor;
import com.apollographql.apollo.internal.RealApolloCall;
import com.apollographql.apollo.internal.RealApolloPrefetch;
import com.apollographql.apollo.internal.ResponseFieldMapperFactory;
import com.apollographql.apollo.internal.cache.http.HttpCache;
import com.apollographql.apollo.internal.cache.normalized.RealApolloStore;
import com.apollographql.apollo.internal.util.ApolloLogger;
Expand Down Expand Up @@ -74,7 +74,7 @@ public static Builder builder() {
private final ApolloStore apolloStore;
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
private final Moshi moshi;
private final Map<Class, ResponseFieldMapper> responseFieldMapperPool = new LinkedHashMap<>();
private final ResponseFieldMapperFactory responseFieldMapperFactory = new ResponseFieldMapperFactory();
private final ExecutorService dispatcher;
private final HttpCachePolicy.Policy defaultHttpCachePolicy;
private final CacheControl defaultCacheControl;
Expand All @@ -100,7 +100,7 @@ private ApolloClient(Builder builder) {
@Override
public <D extends Mutation.Data, T, V extends Mutation.Variables> ApolloMutationCall<T> mutate(
@Nonnull Mutation<D, T, V> mutation) {
return newCall(mutation);
return newCall(mutation).cacheControl(CacheControl.NETWORK_ONLY);
}

@Override
Expand Down Expand Up @@ -155,38 +155,26 @@ Response cachedHttpResponse(String cacheKey) throws IOException {

private <D extends Operation.Data, T, V extends Operation.Variables> RealApolloCall<T> newCall(
@Nonnull Operation<D, T, V> operation) {
ResponseFieldMapper responseFieldMapper = responseFieldMapper(operation);
return RealApolloCall.<T>builder()
.operation(operation)
.serverUrl(serverUrl)
.httpCallFactory(httpCallFactory)
.httpCache(httpCache)
.httpCachePolicy(defaultHttpCachePolicy)
.moshi(moshi)
.responseFieldMapper(responseFieldMapper)
.responseFieldMapperFactory(responseFieldMapperFactory)
.customTypeAdapters(customTypeAdapters)
.apolloStore(apolloStore)
.cacheControl(defaultCacheControl)
.cacheHeaders(defaultCacheHeaders)
.dispatcher(dispatcher)
.logger(logger)
.applicationInterceptors(applicationInterceptors)
.refetchQueries(Collections.<Query>emptyList())
.refetchQueryNames(Collections.<OperationName>emptyList())
.build();
}

private ResponseFieldMapper responseFieldMapper(Operation operation) {
Optional<ResponseFieldMapper> responseFieldMapper;
synchronized (responseFieldMapperPool) {
responseFieldMapper = Optional.fromNullable(responseFieldMapperPool.get(operation.getClass()));
if (!responseFieldMapper.isPresent()) {
responseFieldMapper = Optional.of(operation.responseFieldMapper());
responseFieldMapperPool.put(operation.getClass(), responseFieldMapper.get());
}
}
return responseFieldMapper.get();
}

@SuppressWarnings("WeakerAccess") public static class Builder {
OkHttpClient okHttpClient;
HttpUrl serverUrl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.apollographql.apollo.api.Mutation;
import com.apollographql.apollo.api.OperationName;
import com.apollographql.apollo.api.Query;
import com.apollographql.apollo.cache.CacheHeaders;

import javax.annotation.Nonnull;
Expand All @@ -12,15 +13,21 @@
public interface ApolloMutationCall<T> extends ApolloCall<T> {

/**
* <p>Sets a list of GraphQL query names to be re-fetched once this mutation completed.</p>
* In order to get queries to be re-fetched you must obtain {@link ApolloQueryWatcher} from provided list of
* queries before running this mutation.
* <p>Sets a list of {@link ApolloQueryWatcher} query names to be re-fetched once this mutation completed.</p>
*
* @param operationNames array of {@link OperationName} query names to be re-fetched
* @return {@link ApolloMutationCall} that will trigger re-fetching provided queries
*/
@Nonnull ApolloMutationCall<T> refetchQueries(@Nonnull OperationName... operationNames);

/**
* <p>Sets a list of {@link Query} to be re-fetched once this mutation completed.</p>
*
* @param queries array of {@link Query} to be re-fetched
* @return {@link ApolloMutationCall} that will trigger re-fetching provided queries
*/
@Nonnull ApolloMutationCall<T> refetchQueries(@Nonnull Query... queries);

@Nonnull @Override ApolloMutationCall<T> cacheHeaders(@Nonnull CacheHeaders cacheHeaders);

@Nonnull @Override ApolloMutationCall<T> clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import com.apollographql.apollo.api.internal.Function;
import com.apollographql.apollo.api.internal.Optional;
import com.apollographql.apollo.cache.CacheHeaders;
import com.apollographql.apollo.cache.ApolloCacheHeaders;
import com.apollographql.apollo.cache.CacheHeaders;
import com.apollographql.apollo.cache.normalized.NormalizedCache;
import com.apollographql.apollo.cache.normalized.NormalizedCacheFactory;
import com.apollographql.apollo.cache.normalized.Record;
Expand Down
Loading