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.models.base.LogLevel;
020import com.plivo.api.util.Utils;
021import okhttp3.Credentials;
022import okhttp3.Interceptor;
023import okhttp3.OkHttpClient;
024import okhttp3.Protocol;
025import okhttp3.Response;
026import okhttp3.ResponseBody;
027import okhttp3.logging.HttpLoggingInterceptor;
028import okhttp3.logging.HttpLoggingInterceptor.Level;
029import okhttp3.ConnectionPool;
030import retrofit2.Retrofit;
031import retrofit2.converter.jackson.JacksonConverterFactory;
032
033import java.io.BufferedReader;
034import java.io.IOException;
035import java.io.InputStream;
036import java.io.InputStreamReader;
037import java.net.ProtocolException;
038import java.text.SimpleDateFormat;
039import java.util.concurrent.TimeUnit;
040
041public class PlivoClient {
042
043  private static SimpleModule simpleModule = new SimpleModule();
044  protected static String BASE_URL = "https://api.plivo.com/v1/";
045  protected static String VOICE_BASE_URL = "https://api.plivo.com/v1/";
046  protected static String VOICE_FALLBACK1_URL = "https://api.plivo.com/v1/";
047  protected static String VOICE_FALLBACK2_URL = "https://api.plivo.com/v1/";
048  protected static String CALLINSIGHTS_BASE_URL = "https://stats.plivo.com/v1/";
049  protected static String LOOKUP_BASE_URL = "https://lookup.plivo.com/v1/";
050  private static String version = "Unknown Version";
051  private boolean testing = false;
052  private static ObjectMapper objectMapper = new ObjectMapper();
053
054  public void setTesting(boolean testing) {
055    this.testing = testing;
056  }
057
058  public boolean isTesting() {
059    return testing;
060  }
061
062  static {
063    simpleModule.setDeserializerModifier(new BeanDeserializerModifier() {
064      @Override
065      public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType type,
066                                                        BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
067        return new JsonDeserializer<Enum>() {
068          @Override
069          public Enum deserialize(JsonParser jp, DeserializationContext ctxt)
070            throws IOException, JsonProcessingException {
071            Class<? extends Enum> rawClass = (Class<Enum<?>>) type.getRawClass();
072            return Enum.valueOf(rawClass, jp.getValueAsString().toUpperCase().replace("-", "_"));
073          }
074        };
075      }
076    });
077    simpleModule.addSerializer(Enum.class, new StdSerializer<Enum>(Enum.class) {
078      @Override
079      public void serialize(Enum value, JsonGenerator gen, SerializerProvider provider)
080        throws IOException {
081        gen.writeString(value.name().toLowerCase().replace("_", "-"));
082      }
083    });
084  }
085
086  {
087    try {
088      InputStream inputStream = PlivoClient.class
089        .getResource("version.txt")
090        .openStream();
091
092      version = new BufferedReader(new InputStreamReader(inputStream)).readLine();
093    } catch (IOException ignored) {
094      ignored.printStackTrace();
095    }
096    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
097    objectMapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
098    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
099    objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
100  }
101
102  private Interceptor interceptor;
103  private final String authId;
104  private final String authToken;
105  private OkHttpClient httpClient;
106  private Retrofit retrofit;
107  private Retrofit voiceRetrofit;
108  private Retrofit voiceFallback1Retrofit;
109  private Retrofit voiceFallback2Retrofit;
110  private Retrofit callInsightsRetrofit;
111  private Retrofit lookupRetrofit;
112  private PlivoAPIService apiService = null;
113  private PlivoAPIService voiceApiService = null;
114  private PlivoAPIService voiceFallback1Service = null;
115  private PlivoAPIService voiceFallback2Service = null;
116  private CallInsightsAPIService callInsightsAPIService = null;
117  private LookupAPIService lookupAPIService = null;
118
119  /**
120   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
121   * the timeout and proxy using:
122   *
123   * <pre><code>
124   *   new OkHttpClient.Builder()
125   *   .proxy(proxy)
126   *   .connectTimeout(1, TimeUnit.MINUTES);
127   * </code></pre>
128   *
129   * @param authId
130   * @param authToken
131   * @param httpClientBuilder
132   * @param baseUrl
133   * @param simpleModule
134   * @param logLevel
135   */
136  public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder, final String baseUrl, final SimpleModule simpleModule, final LogLevel logLevel) {
137    if (!(Utils.isAccountIdValid(authId) || Utils.isSubaccountIdValid(authId))) {
138      throw new IllegalArgumentException("invalid account ID");
139    }
140
141    this.authId = authId;
142    this.authToken = authToken;
143    this.objectMapper.registerModule(simpleModule);
144    this.interceptor = new HttpLoggingInterceptor().setLevel(Level.valueOf(logLevel.toString()));
145
146    httpClient = httpClientBuilder
147      .addNetworkInterceptor(interceptor)
148      .addInterceptor(chain -> chain.proceed(
149        chain.request()
150          .newBuilder()
151          .addHeader("Authorization", Credentials.basic(getAuthId(), getAuthToken()))
152          .addHeader("User-Agent", String.format("%s/%s (Implementation: %s %s %s, Specification: %s %s %s)", "plivo-java", version,
153            Runtime.class.getPackage().getImplementationVendor(),
154            Runtime.class.getPackage().getImplementationTitle(),
155            Runtime.class.getPackage().getImplementationVersion(),
156            Runtime.class.getPackage().getSpecificationVendor(),
157            Runtime.class.getPackage().getSpecificationTitle(),
158            Runtime.class.getPackage().getSpecificationVersion()
159          ))
160          .build()
161      ))
162      .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES))
163      .addNetworkInterceptor(chain -> {
164        Response response;
165        try {
166          response = chain.proceed(chain.request());
167        } catch (ProtocolException protocolException) {
168          // We return bodies for HTTP 204!
169          response = new Response.Builder()
170            .request(chain.request())
171            .code(204)
172            .protocol(Protocol.HTTP_1_1)
173            .body(ResponseBody.create(null, new byte[]{}))
174            .build();
175        }
176        return response;
177      }).build();
178
179    retrofit = new Retrofit.Builder()
180      .client(httpClient)
181      .baseUrl(baseUrl)
182      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
183      .build();
184
185    this.apiService = retrofit.create(PlivoAPIService.class);
186
187    voiceRetrofit = new Retrofit.Builder()
188      .client(httpClient)
189      .baseUrl((VOICE_BASE_URL))
190      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
191      .build();
192
193    this.voiceApiService = voiceRetrofit.create(PlivoAPIService.class);
194
195    voiceFallback1Retrofit = new Retrofit.Builder()
196      .client(httpClient)
197      .baseUrl((VOICE_FALLBACK1_URL))
198      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
199      .build();
200
201    this.voiceFallback1Service = voiceFallback1Retrofit.create(PlivoAPIService.class);
202
203    voiceFallback2Retrofit = new Retrofit.Builder()
204      .client(httpClient)
205      .baseUrl((VOICE_FALLBACK2_URL))
206      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
207      .build();
208
209    this.voiceFallback2Service = voiceFallback2Retrofit.create(PlivoAPIService.class);
210
211    callInsightsRetrofit = new Retrofit.Builder()
212      .client(httpClient)
213      .baseUrl((CALLINSIGHTS_BASE_URL))
214      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
215      .build();
216
217    this.callInsightsAPIService = callInsightsRetrofit.create(CallInsightsAPIService.class);
218
219    lookupRetrofit = new Retrofit.Builder()
220      .client(httpClient)
221      .baseUrl((LOOKUP_BASE_URL))
222      .addConverterFactory(JacksonConverterFactory.create(objectMapper))
223      .build();
224
225    this.lookupAPIService = lookupRetrofit.create(LookupAPIService.class);
226  }
227
228  /**
229   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
230   * the timeout and proxy using:
231   *
232   * <pre><code>
233   *   new OkHttpClient.Builder()
234   *   .proxy(proxy)
235   *   .connectTimeout(1, TimeUnit.MINUTES);
236   * </code></pre>
237   *
238   * @param authId
239   * @param authToken
240   */
241  public PlivoClient(String authId, String authToken) {
242    this(authId, authToken, new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS), BASE_URL, simpleModule, LogLevel.NONE);
243  }
244
245  /**
246   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
247   * the timeout and proxy using:
248   *
249   * <pre><code>
250   *   new OkHttpClient.Builder()
251   *   .proxy(proxy)
252   *   .connectTimeout(1, TimeUnit.MINUTES);
253   * </code></pre>
254   *
255   * @param authId
256   * @param authToken
257   * @param httpClientBuilder
258   */
259  public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder) {
260    this(authId, authToken, httpClientBuilder, BASE_URL, simpleModule, LogLevel.NONE);
261  }
262
263  /**
264   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
265   * the timeout and proxy using:
266   *
267   * <pre><code>
268   *    new OkHttpClient.Builder()
269   *    .proxy(proxy)
270   *    .connectTimeout(1, TimeUnit.MINUTES);
271   * </code></pre>
272   *
273   * To set Log level you can pass LogLevel enum.It can be set to following values:
274   *
275   * NONE - No logs
276   * BASIC - Log request and response line
277   * HEADER - Log request and response line along with their headers
278   * BODY - Log request and response line along with their headers and bodies
279   *
280   * @param authId
281   * @param authToken
282   * @param logLevel
283   */
284  public PlivoClient(String authId, String authToken, LogLevel logLevel) {
285    this(authId, authToken, new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS), BASE_URL, simpleModule, logLevel);
286  }
287
288  /**
289   * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set
290   * the timeout and proxy using:
291   *
292   * <pre><code>
293   *   new OkHttpClient.Builder()
294   *   .proxy(proxy)
295   *   .connectTimeout(1, TimeUnit.MINUTES);
296   * </code></pre>
297   * To set Log level you can pass LogLevel enum.It can be set to following values:
298   *
299   * NONE - No logs
300   * BASIC - Log request and response line
301   * HEADER - Log request and response line along with their headers
302   * BODY - Log request and response line along with their headers and bodies
303   *
304   * @param authId
305   * @param authToken
306   * @param httpClientBuilder
307   * @param logLevel
308   */
309  public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder, LogLevel logLevel) {
310    this(authId, authToken, httpClientBuilder, BASE_URL, simpleModule, logLevel);
311  }
312
313  public static ObjectMapper getObjectMapper() {
314    return objectMapper;
315  }
316
317  Retrofit getRetrofit() {
318    return retrofit;
319  }
320
321  public PlivoAPIService getApiService() {
322    return apiService;
323  }
324
325  public PlivoAPIService getVoiceApiService(){
326    return voiceApiService;
327  }
328
329  public PlivoAPIService getVoiceFallback1Service(){
330    return voiceFallback1Service;
331  }
332
333  public PlivoAPIService getVoiceFallback2Service(){
334    return voiceFallback2Service;
335  }
336
337  public CallInsightsAPIService getCallInsightsAPIService() {
338    return callInsightsAPIService;
339  }
340
341  public LookupAPIService getLookupAPIService() {
342    return lookupAPIService;
343  }
344
345  void setApiService(PlivoAPIService apiService) {
346    this.apiService = apiService;
347  }
348
349  public String getAuthId() {
350    return authId;
351  }
352
353  public String getAuthToken() {
354    return authToken;
355  }
356}