2

I am new to GraphQL so I want to test somethings before starting my own project. As a test database I am using Prisma test database(s) and service(s) and Apollo Android as a library in my application. I am almost done with query and mutation. As a next I want to do something with subscription. So, my graphql subscription looks like:

subscription observer {
  post {
    mutation
    node {
      id
      title
      text
      isPublished
    }
  }
}

As Apollo Android library generates some classes based on my graphql code, I tried to use the observer subscription like this:

Interceptor interceptor = chain -> chain.proceed(chain.request());

OkHttpClient ohc = new OkHttpClient.Builder().addInterceptor(interceptor).build();

ApolloClient mApolloClient = ApolloClient.builder().serverUrl(ENDPOINT).okHttpClient(ohc).build();
mApolloClient.subscribe(ObserverSubscription.builder().build()).execute(new ApolloSubscriptionCall.Callback<ObserverSubscription.Data>() {
    @Override
    public void onResponse(@NotNull Response<ObserverSubscription.Data> response) {
        // do work here
    }

    @Override
    public void onFailure(@NotNull ApolloException e) {
        // catch exception
    }

    @Override
    public void onCompleted() {
        // some other stuff goes here
    }
});

When executing this codes following exception thrown:

java.lang.IllegalStateException: Subscription manager is not configured

This message comes from here and it is directly related with this.

My question is there any way to use subscriptions using apollo-android in Android app?

AL.
  • 36,815
  • 10
  • 142
  • 281
Mirjalal
  • 1,292
  • 1
  • 17
  • 40

1 Answers1

3

First, sorry for the late answer, I just got myself stuck with the same problem, and using info from a lot of sites, I made a successful subscription.

When you configure your ApolloClient, you need to define some extra parameters:

First is the normalized cache, for this one I'm using a default config:

 NormalizedCacheFactory normalizedCacheFactory(){
        return new LruNormalizedCacheFactory(EvictionPolicy.builder().maxSizeBytes(10 * 1024).build());
    }

And the cacheKeyResolver:

CacheKeyResolver cacheKeyResolver(){
        return new CacheKeyResolver() {
            @Nonnull
            @Override
            public CacheKey fromFieldRecordSet(@Nonnull ResponseField field, @Nonnull Map<String, Object> recordSet) {
                if (recordSet.containsKey("id")) {
                    String id = (String) recordSet.get("id");
                    return CacheKey.from(id);
                }
                return CacheKey.NO_KEY;
            }

            @Nonnull @Override
            public CacheKey fromFieldArguments(@Nonnull ResponseField field, @Nonnull Operation.Variables variables) {
                return CacheKey.NO_KEY;
            }
        };
    }

On the cacheKeyResolver you need to specify how the IDs from your objects will be detected, on my case, all of them contains "id" at the start of their name. You can add more parameters using the same structure.

Both of this configs go inside the ApolloClient builder:

Map<String, Object> connectionParams = new HashMap<>();
        return ApolloClient.builder()
                .serverUrl(urlBaseDeDatos)
                .normalizedCache(normalizedCacheFactory(),cacheKeyResolver())
                .okHttpClient(okHttpClient())
                .subscriptionConnectionParams(connectionParams)
                .subscriptionTransportFactory(new WebSocketSubscriptionTransport.Factory(urlBaseDeDatos, okHttpClient()))
                .build();

The last 2 are the important ones, since they control your subscription (or I think they do), once I defined those 2, I could make a subscription. My connectionParams are empty since I ain't using an auth token or something like that.

Now, for the subscription:

private Flowable<Response<TaskStatusChangedSubscription.Data>> subscription(){
        apolloClient.clearNormalizedCache();
        return Rx2Apollo.from(apolloClient.subscribe(TaskStatusChangedSubscription
                .builder()
                .build()));
    }

Just a normal flowable with the Apollo.subscribe method and some help from RxJava, and doing a .subscribe() on the same function or on another (like a disposable).

Finally (sorry for all the explanation from before, but it could help someone else), your problem should be solved if you change your apollo builder to this:

Map<String, Object> connectionParams = new HashMap<>();
ApolloClient mApolloClient = ApolloClient.builder().serverUrl(ENDPOINT).okHttpClient(ohc).subscriptionConnectionParams(connectionParams)
                .subscriptionTransportFactory(new WebSocketSubscriptionTransport.Factory(ENDPOINT, okHttpClient()))
                .build();
  • Hi. Sorry for the too late response. Thanks for the answer, but what about the `normalizedCacheFactory()` and `cacheKeyResolver()`method? How did you implement them? – Mirjalal Jul 19 '19 at 11:17
  • @MirjalalTalishinski Edited with the new info, I hope this can help you – Sefron Zilf Jul 20 '19 at 17:58
  • 1
    FYI just adding `subscriptionTransportFactory` is enough to make it "work" – Sam Jan 14 '20 at 15:53