org.dataconservancy.cos.osf.client.retrofit.TestingOsfServiceFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.dataconservancy.cos.osf.client.retrofit.TestingOsfServiceFactory.java

Source

/*
 * Copyright 2016 Johns Hopkins University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dataconservancy.cos.osf.client.retrofit;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.jasminb.jsonapi.ResourceConverter;
import com.github.jasminb.jsonapi.annotations.Type;
import com.github.jasminb.jsonapi.retrofit.JSONAPIConverterFactory;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import org.dataconservancy.cos.osf.client.config.JacksonOsfConfigurationService;
import org.dataconservancy.cos.osf.client.config.JacksonWbConfigurationService;
import org.dataconservancy.cos.osf.client.config.OsfConfigurationService;
import org.dataconservancy.cos.osf.client.config.WbConfigurationService;
import org.dataconservancy.cos.osf.client.support.AuthInterceptor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Factory used to produce clients for testing the OSF API endpoint.  Specifically, this factory uses default
 * implementations for collaborating objects like the HTTP client and OSF Configuration service.  These defaults reduce
 * the boilerplate for tests (and they are the only implementations available).
 * <p>
 * Importantly, this factory exposes the {@link OkHttpClient#interceptors() interceptors} that are used by the HTTP
 * client.  This allows a test class to configure an interceptor and add it to the chain.  By default, this factory
 * configures an instance of {@link AuthInterceptor} on the client.
 * </p>
 * <p>
 * Sample usage:
 * <pre>
 * // create an instance of the factory, referencing a configuration for the client formatted as JSON
 * TestingOsfServiceFactory factory = new TestingOsfServiceFactory("osf-client-local.json");
 *
 * // configure any interceptors on the client (e.g. to mutate HTTP headers)
 * factory.interceptors().add((chain) -> chain.proceed(
 *   chain.request().newBuilder().addHeader(X_RESPONSE_RESOURCE, "project-node-only-ref-rels.json").build()
 * ));
 *
 * // Obtain an instance of the client and execute a request.
 * OsfService svc = factory.getOsfService(OsfService.class)
 * Node n = svc.node("v8x57").execute().body();
 * </pre>
 * </p>
 *
 * @author Elliot Metsger (emetsger@jhu.edu)
 */
public class TestingOsfServiceFactory {

    private final RetrofitOsfServiceFactory factory;

    private final OkHttpClient httpClient;

    private final OsfConfigurationService osfConfigurationService;

    private final WbConfigurationService wbConfigurationService;

    /**
     * Creates a new RetrofitOsfServiceFactory with default implementations for the required collaborators.  Not
     * recommended for production.
     *
     * @param jsonConfigurationResource a classpath resource containing the configuration for the OSF API; must be JSON
     */
    public TestingOsfServiceFactory(final String jsonConfigurationResource) {
        // Configure the configuration service.
        osfConfigurationService = new JacksonOsfConfigurationService(jsonConfigurationResource);
        wbConfigurationService = new JacksonWbConfigurationService(jsonConfigurationResource);

        // Wiring for the RetrofitOsfService Factory

        // ... the OK HTTP client used by Retrofit to make calls
        httpClient = new OkHttpClient();
        httpClient.interceptors()
                .add(new AuthInterceptor(osfConfigurationService.getConfiguration().getAuthHeader()));

        // ... the JSON-API converter used by Retrofit to map JSON documents to Java objects
        final List<Class<?>> domainClasses = new ArrayList<>();

        new FastClasspathScanner("org.dataconservancy.cos.osf.client.model")
                .matchClassesWithAnnotation(Type.class, domainClasses::add).scan();

        final ResourceConverter resourceConverter = new ResourceConverter(new ObjectMapper(),
                domainClasses.toArray(new Class[] {}));

        resourceConverter.setGlobalResolver(relUrl -> {
            final com.squareup.okhttp.Call req = httpClient.newCall(new Request.Builder().url(relUrl).build());
            try {
                return req.execute().body().bytes();
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        });

        final JSONAPIConverterFactory jsonApiConverterFactory = new PaginatedConverterFactory(httpClient,
                resourceConverter);

        factory = new RetrofitOsfServiceFactory(osfConfigurationService, wbConfigurationService, httpClient,
                jsonApiConverterFactory);
    }

    /**
     * Expose a mutable list of {@link Interceptor}s used by the HTTP client to mutate requests and responses.  Clients
     * may wish to mutate this list to add custom interceptors (useful for adding headers, etc.).
     *
     * @return a mutable list of {@code Interceptor}s
     */
    public List<Interceptor> interceptors() {
        return httpClient.interceptors();
    }

    /**
     * Expose the underlying OkHttpClient.  This is the HTTP client shared by every {@code OsfService} instance produced
     * by this factory.  If the returned HTTP client is mutated, behavior is undefined for {@code OsfService} instances
     * previously created by this factory.  Future instances of {@code OsfService} will use any settings that have been
     * set by the caller.
     *
     * @return the shared OkHttpClient
     */
    public OkHttpClient getHttpClient() {
        return httpClient;
    }

    /**
     * Expose the {@link OsfConfigurationService OSF configuration service} used by this factory.
     *
     * @return the OSF configuration service
     */
    public OsfConfigurationService getConfigurationService() {
        return osfConfigurationService;
    }

    /**
     * Expose the {@link WbConfigurationService Waterbutler configuration service} used by this factory.
     *
     * @return the Waterbutler configuration service
     */
    public WbConfigurationService getWbConfigurationService() {
        return wbConfigurationService;
    }

    /**
     * Answers a configured Retrofit-based OSF client that communicates with the OSF V2 API at the base URL obtained
     * from the OSF configuration service.  The JSON responses from the OSF V2 API will be mapped to Java objects
     * according to the JSON API converter factory.  HTTP requests and responses are sent via the OK HTTP client.
     *
     * @param osfService the Retrofit interface that contains the methods used by the developer to communicate with the
     *                   OSF API.
     * @param <T>        the Retrofit interface type
     * @return a configured Retrofit interface, ready to service requests.
     */
    public <T> T getOsfService(final Class<T> osfService) {
        return factory.getOsfService(osfService);
    }
}