001package com.plivo.api;
002
003import com.fasterxml.jackson.core.JsonGenerator;
004import com.fasterxml.jackson.core.JsonParser;
005import com.fasterxml.jackson.core.JsonProcessingException;
006import com.fasterxml.jackson.databind.BeanDescription;
007import com.fasterxml.jackson.databind.DeserializationConfig;
008import com.fasterxml.jackson.databind.DeserializationContext;
009import com.fasterxml.jackson.databind.DeserializationFeature;
010import com.fasterxml.jackson.databind.JavaType;
011import com.fasterxml.jackson.databind.JsonDeserializer;
012import com.fasterxml.jackson.databind.ObjectMapper;
013import com.fasterxml.jackson.databind.PropertyNamingStrategy;
014import com.fasterxml.jackson.databind.SerializationFeature;
015import com.fasterxml.jackson.databind.SerializerProvider;
016import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
017import com.fasterxml.jackson.databind.module.SimpleModule;
018import com.fasterxml.jackson.databind.ser.std.StdSerializer;
019import com.plivo.api.util.Utils;
020import okhttp3.Credentials;
021import okhttp3.Interceptor;
022import okhttp3.OkHttpClient;
023import okhttp3.Protocol;
024import okhttp3.Response;
025import okhttp3.ResponseBody;
026import okhttp3.logging.HttpLoggingInterceptor;
027import okhttp3.logging.HttpLoggingInterceptor.Level;
028import retrofit2.Retrofit;
029import retrofit2.converter.jackson.JacksonConverterFactory;
030
031import java.io.BufferedReader;
032import java.io.IOException;
033import java.io.InputStream;
034import java.io.InputStreamReader;
035import java.net.ProtocolException;
036import java.text.SimpleDateFormat;
037
038public class PlivoClient {
039
040  private static SimpleModule simpleModule = new SimpleModule();
041  protected static String BASE_URL = "https://api.plivo.com/v1/";
042  private static String version = "Unknown Version";
043  private boolean testing = false;
044  private static ObjectMapper objectMapper = new ObjectMapper();
045
046  public void setTesting(boolean testing) {
047    this.testing = testing;
048  }
049
050  public boolean isTesting() {
051    return testing;
052  }
053
054  static {
055    simpleModule.setDeserializerModifier(new BeanDeserializerModifier() {
056      @Override
057      public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType type,
058                                                        BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
059        return new JsonDeserializer<Enum>() {
060          @Override
061          public Enum deserialize(JsonParser jp, DeserializationContext ctxt)
062            throws IOException, JsonProcessingException {
063            Class<? extends Enum> rawClass = (Class<Enum<?>>) type.getRawClass();
064            return Enum.valueOf(rawClass, jp.getValueAsString().toUpperCase().replace("-", "_"));
065          }
066        };
067      }
068    });
069    simpleModule.addSerializer(Enum.class, new StdSerializer<Enum>(Enum.class) {
070      @Override
071      public void serialize(Enum value, JsonGenerator gen, SerializerProvider provider)
072        throws IOException {
073        gen.writeString(value.name().toLowerCase().replace("_", "-"));
074      }
075    });
076  }
077
078  {
079    try {
080      InputStream inputStream = PlivoClient.class
081        .getResource("version.txt")
082        .openStream();
083
084      version = new BufferedReader(new InputStreamReader(inputStream)).readLine();
085    } catch (IOException ignored) {
086      ignored.printStackTrace();
087    }
088    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
089    objectMapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
090    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
091    objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
092  }
093
094  private final Interceptor interceptor = new HttpLoggingInterceptor()
095    .setLevel(Level.BODY);
096  private final String authId;
097  private final String authToken;
098  private OkHttpClient httpClient;
099  private Retrofit retrofit;
100  private PlivoAPIService apiService = null;
101
102  /**
103   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
104   * the timeout and proxy using:
105   *
106   * <pre><code>
107   *   new OkHttpClient.Builder()
108   *   .proxy(proxy)
109   *   .connectTimeout(1, TimeUnit.MINUTES);
110   * </code></pre>
111   *
112   * @param authId
113   * @param authToken
114   * @param httpClientBuilder
115   * @param baseUrl
116   * @param simpleModule
117   */
118  public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder, final String baseUrl, final SimpleModule simpleModule) {
119    if (!(Utils.isAccountIdValid(authId) || Utils.isSubaccountIdValid(authId))) {
120      throw new IllegalArgumentException("invalid account ID");
121    }
122
123    this.authId = authId;
124    this.authToken = authToken;
125    this.objectMapper.registerModule(simpleModule);
126
127    httpClient = httpClientBuilder
128      .addNetworkInterceptor(interceptor)
129      .addInterceptor(chain -> chain.proceed(
130        chain.request()
131          .newBuilder()
132          .addHeader("Authorization", Credentials.basic(getAuthId(), getAuthToken()))
133          .addHeader("User-Agent", String.format("%s/%s (Implementation: %s %s %s, Specification: %s %s %s)", "plivo-java", version,
134            Runtime.class.getPackage().getImplementationVendor(),
135            Runtime.class.getPackage().getImplementationTitle(),
136            Runtime.class.getPackage().getImplementationVersion(),
137            Runtime.class.getPackage().getSpecificationVendor(),
138            Runtime.class.getPackage().getSpecificationTitle(),
139            Runtime.class.getPackage().getSpecificationVersion()
140          ))
141          .build()
142      ))
143      .addNetworkInterceptor(chain -> {
144        Response response;
145        try {
146          response = chain.proceed(chain.request());
147        } catch (ProtocolException protocolException) {
148          // We return bodies for HTTP 204!
149          response = new Response.Builder()
150            .request(chain.request())
151            .code(204)
152            .protocol(Protocol.HTTP_1_1)
153            .body(ResponseBody.create(null, new byte[]{}))
154            .build();
155        }
156        return response;
157      }).build();
158
159    retrofit = new Retrofit.Builder()
160      .client(httpClient)
161      .baseUrl(baseUrl)
162      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
163      .build();
164
165    this.apiService = retrofit.create(PlivoAPIService.class);
166  }
167
168  /**
169   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
170   * the timeout and proxy using:
171   *
172   * <pre><code>
173   *   new OkHttpClient.Builder()
174   *   .proxy(proxy)
175   *   .connectTimeout(1, TimeUnit.MINUTES);
176   * </code></pre>
177   *
178   * @param authId
179   * @param authToken
180   */
181  public PlivoClient(String authId, String authToken) {
182    this(authId, authToken, new OkHttpClient.Builder(), BASE_URL, simpleModule);
183  }
184
185  /**
186   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
187   * the timeout and proxy using:
188   *
189   * <pre><code>
190   *   new OkHttpClient.Builder()
191   *   .proxy(proxy)
192   *   .connectTimeout(1, TimeUnit.MINUTES);
193   * </code></pre>
194   *
195   * @param authId
196   * @param authToken
197   * @param httpClientBuilder
198   */
199  public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder) {
200    this(authId, authToken, httpClientBuilder, BASE_URL, simpleModule);
201  }
202
203  public static ObjectMapper getObjectMapper() {
204    return objectMapper;
205  }
206
207  Retrofit getRetrofit() {
208    return retrofit;
209  }
210
211  public PlivoAPIService getApiService() {
212    return apiService;
213  }
214
215  void setApiService(PlivoAPIService apiService) {
216    this.apiService = apiService;
217  }
218
219  public String getAuthId() {
220    return authId;
221  }
222
223  public String getAuthToken() {
224    return authToken;
225  }
226}