All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ai.api.AIDataService Maven / Gradle / Ivy

Go to download

API.AI Java SDK allows using voice commands and integration with dialog scenarios defined for a particular agent in API.AI.

There is a newer version: 1.6.12
Show newest version
package ai.api;

/***********************************************************************************************************************
 *
 * API.AI Java SDK - client-side libraries for API.AI
 * =================================================
 *
 * Copyright (C) 2015 by Speaktoit, Inc. (https://www.speaktoit.com) https://www.api.ai
 *
 * *********************************************************************************************************************
 *
 * 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.
 *
 ***********************************************************************************************************************/

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;

import ai.api.util.IOUtils;
import ai.api.util.StringUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.*;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ai.api.http.HttpClient;
import ai.api.model.AIContext;
import ai.api.model.AIRequest;
import ai.api.model.AIResponse;
import ai.api.model.Entity;
import ai.api.model.Status;

/**
 * Do simple requests to the AI Service
 */
public class AIDataService {

  private static final Logger Log = LogManager.getLogger(AIDataService.class);
  private static final AIServiceContext UNDEFINED_SERVICE_CONTEXT = null;
  private static final String REQUEST_METHOD_POST = "POST";
  private static final String REQUEST_METHOD_DELETE = "DELETE";
  private static final String REQUEST_METHOD_GET = "GET";
  private static final String DEFAULT_REQUEST_METHOD = REQUEST_METHOD_POST;

  /**
   * Cannot be null
   */
  private final static Gson GSON = GsonFactory.getDefaultFactory().getGson();

  /**
   * Cannot be null
   */
  private final AIConfiguration config;

  /**
   * Cannot be null
   */
  private final AIServiceContext defaultServiceContext;

  /**
   * Create new service for given configuration and some predefined service context
   * 
   * @param config Service configuration data. Cannot be null
   * @param serviceContext Service context. If null then new context will be created
   * @throws IllegalArgumentException If config parameter is null
   */
  public AIDataService(final AIConfiguration config, final AIServiceContext serviceContext) {
    if (config == null) {
      throw new IllegalArgumentException("config should not be null");
    }
    this.config = config.clone();
    if (serviceContext == null) {
      this.defaultServiceContext = new AIServiceContextBuilder().generateSessionId().build();
    } else {
      this.defaultServiceContext = serviceContext;
    }
  }

  /**
   * Create new service with unique context for given configuration
   * 
   * @param config Service configuration data. Cannot be null
   * @throws IllegalArgumentException If config parameter is null
   */
  public AIDataService(final AIConfiguration config) {
    this(config, null);
  }

  /**
   * @return Current context used in each request. Never null
   */
  public AIServiceContext getContext() {
    return defaultServiceContext;
  }

  /**
   * Make request to the AI service.
   *
   * @param request request object to the service. Cannot be null
   * @return response object from service. Never null
   */
  public AIResponse request(final AIRequest request) throws AIServiceException {
    return request(request, (RequestExtras) null);
  }

  /**
   * Make request to the AI service.
   *
   * @param request request object to the service. Cannot be null
   * @param serviceContext custom service context that should be used instead of the default context
   * @return response object from service. Never null
   */
  public AIResponse request(final AIRequest request, final AIServiceContext serviceContext)
      throws AIServiceException {
    return request(request, (RequestExtras) null, serviceContext);
  }

  /**
   * Make request to the AI service.
   *
   * @param request request object to the service. Cannot be null
   * @param requestExtras object that can hold additional contexts and entities
   * @return response object from service. Never null
   */
  public AIResponse request(final AIRequest request, final RequestExtras requestExtras)
      throws AIServiceException {
    return request(request, requestExtras, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Make request to the AI service.
   *
   * @param request request object to the service. Cannot be null
   * @param requestExtras object that can hold additional contexts and entities
   * @param serviceContext custom service context that should be used instead of the default context
   * @return response object from service. Never null
   */
  public AIResponse request(final AIRequest request, final RequestExtras requestExtras,
      final AIServiceContext serviceContext) throws AIServiceException {
    if (request == null) {
      throw new IllegalArgumentException("Request argument must not be null");
    }

    Log.debug("Start request");

    try {

      request.setLanguage(config.getApiAiLanguage());
      request.setSessionId(getSessionId(serviceContext));
      if (StringUtils.isEmpty(request.getTimezone())) {
        request.setTimezone(getTimeZone(serviceContext));
      }

      Map additionalHeaders = null;

      if (requestExtras != null) {
        fillRequest(request, requestExtras);
        additionalHeaders = requestExtras.getAdditionalHeaders();
      }

      final String queryData = GSON.toJson(request);
      final String response = doTextRequest(config.getQuestionUrl(getSessionId(serviceContext)),
          queryData, additionalHeaders);

      if (StringUtils.isEmpty(response)) {
        throw new AIServiceException(
            "Empty response from ai service. Please check configuration and Internet connection.");
      }

      Log.debug("Response json: " + response.replaceAll("[\r\n]+", " "));

      final AIResponse aiResponse = GSON.fromJson(response, AIResponse.class);

      if (aiResponse == null) {
        throw new AIServiceException(
            "API.AI response parsed as null. Check debug log for details.");
      }

      if (aiResponse.isError()) {
        throw new AIServiceException(aiResponse);
      }

      aiResponse.cleanup();

      return aiResponse;

    } catch (final MalformedURLException e) {
      Log.error("Malformed url should not be raised", e);
      throw new AIServiceException("Wrong configuration. Please, connect to API.AI Service support",
          e);
    } catch (final JsonSyntaxException je) {
      throw new AIServiceException(
          "Wrong service answer format. Please, connect to API.AI Service support", je);
    }

  }

  /**
   * Make requests to the AI service with voice data.
   *
   * @param voiceStream voice data stream for recognition. Cannot be null
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse voiceRequest(final InputStream voiceStream) throws AIServiceException {
    return voiceRequest(voiceStream, new RequestExtras());
  }

  /**
   * Make requests to the AI service with voice data.
   *
   * @param voiceStream voice data stream for recognition. Cannot be null
   * @param aiContexts additional contexts for request
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse voiceRequest(final InputStream voiceStream, final List aiContexts)
      throws AIServiceException {
    return voiceRequest(voiceStream, new RequestExtras(aiContexts, null));
  }

  /**
   * Make requests to the AI service with voice data.
   *
   * @param voiceStream voice data stream for recognition. Cannot be null
   * @param requestExtras object that can hold additional contexts and entities
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse voiceRequest(final InputStream voiceStream, final RequestExtras requestExtras)
      throws AIServiceException {
    return voiceRequest(voiceStream, requestExtras, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Make requests to the AI service with voice data.
   *
   * @param voiceStream voice data stream for recognition. Cannot be null
   * @param requestExtras object that can hold additional contexts and entities
   * @param serviceContext custom service context that should be used instead of the default context
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse voiceRequest(final InputStream voiceStream, final RequestExtras requestExtras,
      final AIServiceContext serviceContext) throws AIServiceException {
    assert voiceStream != null;
    Log.debug("Start voice request");

    try {
      final AIRequest request = new AIRequest();

      request.setLanguage(config.getApiAiLanguage());
      request.setSessionId(getSessionId(serviceContext));
      request.setTimezone(getTimeZone(serviceContext));

      Map additionalHeaders = null;

      if (requestExtras != null) {
        fillRequest(request, requestExtras);
        additionalHeaders = requestExtras.getAdditionalHeaders();
      }

      final String queryData = GSON.toJson(request);

      Log.debug("Request json: " + queryData);

      final String response = doSoundRequest(voiceStream, queryData, additionalHeaders);

      if (StringUtils.isEmpty(response)) {
        throw new AIServiceException("Empty response from ai service. Please check configuration.");
      }

      Log.debug("Response json: " + response);

      final AIResponse aiResponse = GSON.fromJson(response, AIResponse.class);

      if (aiResponse == null) {
        throw new AIServiceException(
            "API.AI response parsed as null. Check debug log for details.");
      }

      if (aiResponse.isError()) {
        throw new AIServiceException(aiResponse);
      }

      aiResponse.cleanup();

      return aiResponse;

    } catch (final MalformedURLException e) {
      Log.error("Malformed url should not be raised", e);
      throw new AIServiceException("Wrong configuration. Please, connect to AI Service support", e);
    } catch (final JsonSyntaxException je) {
      throw new AIServiceException(
          "Wrong service answer format. Please, connect to API.AI Service support", je);
    }
  }

  /**
   * Forget all old contexts
   *
   * @return true if operation succeed, false otherwise
   */
  @Deprecated
  public boolean resetContexts() {
    final AIRequest cleanRequest = new AIRequest();
    cleanRequest.setQuery("empty_query_for_resetting_contexts"); // TODO remove it after protocol
                                                                 // fix
    cleanRequest.setResetContexts(true);
    try {
      final AIResponse response = request(cleanRequest);
      return !response.isError();
    } catch (final AIServiceException e) {
      Log.error("Exception while contexts clean.", e);
      return false;
    }
  }

  /**
   * Retrieves the list of all currently active contexts for a session
   * 
   * @return List of contexts, or empty list if there is no any active contexts
   * @throws AIServiceException
   */
  public List getActiveContexts() throws AIServiceException {
    return getActiveContexts(UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Retrieves the list of all currently active contexts for a session
   * 
   * @param serviceContext custom service context that should be used instead of the default context
   * @return List of contexts, or empty list if there is no any active contexts
   * @throws AIServiceException
   */
  public List getActiveContexts(final AIServiceContext serviceContext)
      throws AIServiceException {
    try {
      return doRequest(ApiActiveContextListResponse.class,
          config.getContextsUrl(getSessionId(serviceContext)), REQUEST_METHOD_GET);
    } catch (BadResponseStatusException e) {
      throw new AIServiceException(e.response);
    }
  }

  /**
   * Retrieves the specified context for a session
   * 
   * @param contextName The context name
   * @return null if context not found
   * @throws AIServiceException
   */
  public AIContext getActiveContext(final String contextName) throws AIServiceException {
    return getActiveContext(contextName, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Retrieves the specified context for a session
   * 
   * @param contextName The context name
   * @param serviceContext custom service context that should be used instead of the default context
   * @return null if context not found
   * @throws AIServiceException
   */
  public AIContext getActiveContext(final String contextName, final AIServiceContext serviceContext)
      throws AIServiceException {
    try {
      return doRequest(AIContext.class,
          config.getContextsUrl(getSessionId(serviceContext), contextName), REQUEST_METHOD_GET);
    } catch (BadResponseStatusException e) {
      if (e.response.getStatus().getCode() == 404) {
        return null;
      } else {
        throw new AIServiceException(e.response);
      }
    }
  }

  /**
   * Adds new active contexts for a session
   * 
   * @param contexts Iterable collection of contexts
   * @return List of added context names, or empty list if no contexts were added
   * @throws AIServiceException
   */
  public List addActiveContext(final Iterable contexts)
      throws AIServiceException {
    return addActiveContext(contexts, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Adds new active contexts for a session
   * 
   * @param contexts Iterable collection of contexts
   * @param serviceContext custom service context that should be used instead of the default context
   * @return List of added context names, or empty list if no contexts were added
   * @throws AIServiceException
   */
  public List addActiveContext(final Iterable contexts,
      final AIServiceContext serviceContext) throws AIServiceException {
    ApiActiveContextNamesResponse response;
    try {
      response = doRequest(contexts, ApiActiveContextNamesResponse.class,
          config.getContextsUrl(getSessionId(serviceContext)), REQUEST_METHOD_POST);
      return response.names;
    } catch (BadResponseStatusException e) {
      throw new AIServiceException(e.response);
    }
  }

  /**
   * Adds new active context for a session
   * 
   * @param context New context
   * @return Name of added context
   * @throws AIServiceException
   */
  public String addActiveContext(final AIContext context) throws AIServiceException {
    return addActiveContext(context, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Adds new active context for a session
   * 
   * @param context New context
   * @param serviceContext custom service context that should be used instead of the default context
   * @return Name of added context
   * @throws AIServiceException
   */
  public String addActiveContext(final AIContext context, final AIServiceContext serviceContext)
      throws AIServiceException {
    ApiActiveContextNamesResponse response;
    try {
      response = doRequest(context, ApiActiveContextNamesResponse.class,
          config.getContextsUrl(getSessionId(serviceContext)), REQUEST_METHOD_POST);
      return response.names != null && response.names.size() > 0 ? response.names.get(0) : null;
    } catch (BadResponseStatusException e) {
      throw new AIServiceException(e.response);
    }
  }

  /**
   * Deletes all active contexts for a session
   * 
   * @throws AIServiceException
   */
  public void resetActiveContexts() throws AIServiceException {
    resetActiveContexts(UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Deletes all active contexts for a session
   * 
   * @param serviceContext custom service context that should be used instead of the default context
   * @throws AIServiceException
   */
  public void resetActiveContexts(final AIServiceContext serviceContext) throws AIServiceException {
    try {
      doRequest(AIResponse.class, config.getContextsUrl(getSessionId(serviceContext)),
          REQUEST_METHOD_DELETE);
    } catch (BadResponseStatusException e) {
      throw new AIServiceException(e.response);
    }
  }

  /**
   * Deletes the specified context for a session
   * 
   * @param contextName The context name
   * @return false if context was not delete, true in otherwise case
   * @throws AIServiceException
   */
  public boolean removeActiveContext(final String contextName) throws AIServiceException {
    return removeActiveContext(contextName, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Deletes the specified context for a session
   * 
   * @param contextName The context name
   * @param serviceContext custom service context that should be used instead of the default context
   * @return false if context was not delete, true in otherwise case
   * @throws AIServiceException
   */
  public boolean removeActiveContext(final String contextName,
      final AIServiceContext serviceContext) throws AIServiceException {

    try {
      doRequest(AIResponse.class, config.getContextsUrl(getSessionId(serviceContext), contextName),
          REQUEST_METHOD_DELETE);
      return true;
    } catch (BadResponseStatusException e) {
      if (e.response.getStatus().getCode() == 404) {
        return false;
      } else {
        throw new AIServiceException(e.response);
      }
    }
  }

  /**
   * Add new entity to an agent entity list
   * 
   * @param userEntity new entity data
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse uploadUserEntity(final Entity userEntity) throws AIServiceException {
    return uploadUserEntity(userEntity, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Add new entity to an agent entity list
   * 
   * @param userEntity new entity data
   * @param serviceContext custom service context that should be used instead of the default context
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse uploadUserEntity(final Entity userEntity, AIServiceContext serviceContext)
      throws AIServiceException {
    return uploadUserEntities(Collections.singleton(userEntity), serviceContext);
  }

  /**
   * Add a bunch of new entity to an agent entity list
   * 
   * @param userEntities collection of a new entity data
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse uploadUserEntities(final Collection userEntities)
      throws AIServiceException {
    return uploadUserEntities(userEntities, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Add a bunch of new entity to an agent entity list
   * 
   * @param userEntities collection of a new entity data
   * @param serviceContext custom service context that should be used instead of the default context
   * @return response object from service. Never null
   * @throws AIServiceException
   */
  public AIResponse uploadUserEntities(final Collection userEntities,
      AIServiceContext serviceContext) throws AIServiceException {
    if (userEntities == null || userEntities.size() == 0) {
      throw new AIServiceException("Empty entities list");
    }

    final String requestData = GSON.toJson(userEntities);
    try {
      final String response =
          doTextRequest(config.getUserEntitiesEndpoint(getSessionId(serviceContext)), requestData);
      if (StringUtils.isEmpty(response)) {
        throw new AIServiceException(
            "Empty response from ai service. Please check configuration and Internet connection.");
      }
      Log.debug("Response json: " + response);

      final AIResponse aiResponse = GSON.fromJson(response, AIResponse.class);

      if (aiResponse == null) {
        throw new AIServiceException(
            "API.AI response parsed as null. Check debug log for details.");
      }

      if (aiResponse.isError()) {
        throw new AIServiceException(aiResponse);
      }

      aiResponse.cleanup();
      return aiResponse;

    } catch (final MalformedURLException e) {
      Log.error("Malformed url should not be raised", e);
      throw new AIServiceException("Wrong configuration. Please, connect to AI Service support", e);
    } catch (final JsonSyntaxException je) {
      throw new AIServiceException(
          "Wrong service answer format. Please, connect to API.AI Service support", je);
    }
  }

  /**
   * @param requestJson Cannot be null
   * @param serviceContext custom service context that should be used instead of the default context
   * @return Response string
   * @throws MalformedURLException
   * @throws AIServiceException
   */
  protected String doTextRequest(final String requestJson, AIServiceContext serviceContext)
      throws MalformedURLException, AIServiceException {
    return doTextRequest(config.getQuestionUrl(getSessionId(serviceContext)), requestJson);
  }

  /**
   * @param requestJson Cannot be null
   * @return Response string
   * @throws MalformedURLException
   * @throws AIServiceException
   */
  protected String doTextRequest(final String requestJson)
      throws MalformedURLException, AIServiceException {
    return doTextRequest(requestJson, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * @param endpoint Cannot be null
   * @param requestJson Cannot be null
   * @return Response string
   * @throws MalformedURLException
   * @throws AIServiceException
   */
  protected String doTextRequest(final String endpoint, final String requestJson)
      throws MalformedURLException, AIServiceException {
    return doTextRequest(endpoint, requestJson, null);
  }

  /**
   * @param endpoint Cannot be null
   * @param requestJson Cannot be null
   * @param additionalHeaders
   * @return Response string
   * @throws MalformedURLException
   * @throws AIServiceException
   */
  protected String doTextRequest(final String endpoint, final String requestJson,
      final Map additionalHeaders)
      throws MalformedURLException, AIServiceException {
    // TODO call doRequest method
    assert endpoint != null;
    assert requestJson != null;
    HttpURLConnection connection = null;

    try {

      final URL url = new URL(endpoint);

      final String queryData = requestJson;

      Log.debug("Request json: " + queryData);

      if (config.getProxy() != null) {
        connection = (HttpURLConnection) url.openConnection(config.getProxy());
      } else {
        connection = (HttpURLConnection) url.openConnection();
      }

      connection.setRequestMethod("POST");
      connection.setDoOutput(true);
      connection.addRequestProperty("Authorization", "Bearer " + config.getApiKey());
      connection.addRequestProperty("Content-Type", "application/json; charset=utf-8");
      connection.addRequestProperty("Accept", "application/json");

      if (additionalHeaders != null) {
        for (final Map.Entry entry : additionalHeaders.entrySet()) {
          connection.addRequestProperty(entry.getKey(), entry.getValue());
        }
      }

      connection.connect();

      final BufferedOutputStream outputStream =
          new BufferedOutputStream(connection.getOutputStream());
      IOUtils.writeAll(queryData, outputStream);
      outputStream.close();

      final InputStream inputStream = new BufferedInputStream(connection.getInputStream());
      final String response = IOUtils.readAll(inputStream);
      inputStream.close();

      return response;
    } catch (final IOException e) {
      if (connection != null) {
        try {
          final InputStream errorStream = connection.getErrorStream();
          if (errorStream != null) {
            final String errorString = IOUtils.readAll(errorStream);
            Log.debug(errorString);
            return errorString;
          } else {
            throw new AIServiceException("Can't connect to the api.ai service.", e);
          }
        } catch (final IOException ex) {
          Log.warn("Can't read error response", ex);
        }
      }
      Log.error(
          "Can't make request to the API.AI service. Please, check connection settings and API access token.",
          e);
      throw new AIServiceException(
          "Can't make request to the API.AI service. Please, check connection settings and API access token.",
          e);

    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }

  }

  /**
   * Method extracted for testing purposes
   * 
   * @param voiceStream Cannot be null
   * @param queryData Cannot be null
   */
  protected String doSoundRequest(final InputStream voiceStream, final String queryData)
      throws MalformedURLException, AIServiceException {
    return doSoundRequest(voiceStream, queryData, null, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Method extracted for testing purposes
   * 
   * @param voiceStream Cannot be null
   * @param queryData Cannot be null
   */
  protected String doSoundRequest(final InputStream voiceStream, final String queryData,
      final Map additionalHeaders)
      throws MalformedURLException, AIServiceException {
    return doSoundRequest(voiceStream, queryData, additionalHeaders, UNDEFINED_SERVICE_CONTEXT);
  }

  /**
   * Method extracted for testing purposes
   * 
   * @param voiceStream Cannot be null
   * @param queryData Cannot be null
   */
  protected String doSoundRequest(final InputStream voiceStream, final String queryData,
      final Map additionalHeaders, final AIServiceContext serviceContext)
      throws MalformedURLException, AIServiceException {

    // TODO call doRequest method
    assert voiceStream != null;
    assert queryData != null;
    HttpURLConnection connection = null;
    HttpClient httpClient = null;

    try {
      final URL url = new URL(config.getQuestionUrl(getSessionId(serviceContext)));

      Log.debug("Connecting to {}", url);

      if (config.getProxy() != null) {
        connection = (HttpURLConnection) url.openConnection(config.getProxy());
      } else {
        connection = (HttpURLConnection) url.openConnection();
      }

      connection.addRequestProperty("Authorization", "Bearer " + config.getApiKey());
      connection.addRequestProperty("Accept", "application/json");

      if (additionalHeaders != null) {
        for (final Map.Entry entry : additionalHeaders.entrySet()) {
          connection.addRequestProperty(entry.getKey(), entry.getValue());
        }
      }

      connection.setRequestMethod("POST");
      connection.setDoInput(true);
      connection.setDoOutput(true);

      httpClient = new HttpClient(connection);
      httpClient.setWriteSoundLog(config.isWriteSoundLog());

      httpClient.connectForMultipart();
      httpClient.addFormPart("request", queryData);
      httpClient.addFilePart("voiceData", "voice.wav", voiceStream);
      httpClient.finishMultipart();

      final String response = httpClient.getResponse();
      return response;

    } catch (final IOException e) {
      if (httpClient != null) {
        final String errorString = httpClient.getErrorString();
        Log.debug(errorString);
        if (!StringUtils.isEmpty(errorString)) {
          return errorString;
        } else if (e instanceof HttpRetryException) {
          final AIResponse response = new AIResponse();
          final int code = ((HttpRetryException) e).responseCode();
          final Status status = Status.fromResponseCode(code);
          status.setErrorDetails(((HttpRetryException) e).getReason());
          response.setStatus(status);
          throw new AIServiceException(response);
        }
      }

      Log.error(
          "Can't make request to the API.AI service. Please, check connection settings and API.AI keys.",
          e);
      throw new AIServiceException(
          "Can't make request to the API.AI service. Please, check connection settings and API.AI keys.",
          e);
    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }
  }

  protected  TResponse doRequest(final Type responseType, final String endpoint,
      final String method) throws AIServiceException, BadResponseStatusException {
    return doRequest(responseType, endpoint, method, (Map) null);
  }

  protected  TResponse doRequest(final TRequest request,
      final Type responseType, final String endpoint, final String method)
      throws AIServiceException, BadResponseStatusException {
    return doRequest(request, responseType, endpoint, method, (Map) null);
  }

  protected  TResponse doRequest(final Type responseType, final String endpoint,
      final String method, final Map additionalHeaders)
      throws AIServiceException, BadResponseStatusException {
    return doRequest((Object) null, responseType, endpoint, method, additionalHeaders);
  }

  protected  TResponse doRequest(final TRequest request,
      final Type responseType, final String endpoint, final String method,
      final Map additionalHeaders)
      throws AIServiceException, BadResponseStatusException {

    assert endpoint != null;
    HttpURLConnection connection = null;

    try {

      final URL url = new URL(endpoint);

      final String queryData = request != null ? GSON.toJson(request) : null;
      final String requestMethod = method != null ? method : DEFAULT_REQUEST_METHOD;


      Log.debug("Request json: " + queryData);

      if (config.getProxy() != null) {
        connection = (HttpURLConnection) url.openConnection(config.getProxy());
      } else {
        connection = (HttpURLConnection) url.openConnection();
      }

      if (queryData != null && !REQUEST_METHOD_POST.equals(requestMethod)) {
        throw new AIServiceException("Non-empty request should be sent using POST method");
      }

      connection.setRequestMethod(requestMethod);
      if (REQUEST_METHOD_POST.equals(requestMethod)) {
        connection.setDoOutput(true);
      }
      connection.addRequestProperty("Authorization", "Bearer " + config.getApiKey());
      connection.addRequestProperty("Content-Type", "application/json; charset=utf-8");
      connection.addRequestProperty("Accept", "application/json");

      if (additionalHeaders != null) {
        for (final Map.Entry entry : additionalHeaders.entrySet()) {
          connection.addRequestProperty(entry.getKey(), entry.getValue());
        }
      }

      connection.connect();

      if (queryData != null) {
        final BufferedOutputStream outputStream =
            new BufferedOutputStream(connection.getOutputStream());
        IOUtils.writeAll(queryData, outputStream);
        outputStream.close();
      }

      final InputStream inputStream = new BufferedInputStream(connection.getInputStream());
      final String response = IOUtils.readAll(inputStream);
      inputStream.close();

      try {
        AIResponse aiResponse = GSON.fromJson(response, AIResponse.class);

        if (aiResponse.getStatus() != null && aiResponse.getStatus().getCode() != 200) {
          throw new BadResponseStatusException(aiResponse);
        }
      } catch (JsonParseException e) {
        // response is not in a expected format
      }

      return GSON.fromJson(response, responseType);
    } catch (final IOException e) {
      if (connection != null) {
        try {
          final InputStream errorStream = connection.getErrorStream();
          if (errorStream != null) {
            final String errorString = IOUtils.readAll(errorStream);
            Log.debug(errorString);
            throw new AIServiceException(errorString, e);
          } else {
            throw new AIServiceException("Can't connect to the api.ai service.", e);
          }
        } catch (final IOException ex) {
          Log.warn("Can't read error response", ex);
        }
      }
      Log.error(
          "Can't make request to the API.AI service. Please, check connection settings and API access token.",
          e);
      throw new AIServiceException(
          "Can't make request to the API.AI service. Please, check connection settings and API access token.",
          e);

    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }
  }

  private void fillRequest(final AIRequest request, final RequestExtras requestExtras) {
    assert request != null;
    assert requestExtras != null;
    if (requestExtras.hasContexts()) {
      request.setContexts(requestExtras.getContexts());
    }

    if (requestExtras.hasEntities()) {
      request.setEntities(requestExtras.getEntities());
    }

    if (requestExtras.getLocation() != null) {
      request.setLocation(requestExtras.getLocation());
    }
  }

  private String getSessionId(AIServiceContext serviceContext) {
    return serviceContext != null ? serviceContext.getSessionId()
        : defaultServiceContext.getSessionId();
  }

  private String getTimeZone(AIServiceContext serviceContext) {
    TimeZone timeZone = serviceContext != null
        ? serviceContext.getTimeZone()
        : defaultServiceContext.getTimeZone();
    return (timeZone != null ? timeZone : Calendar.getInstance().getTimeZone()).getID();
  }

  private static class ApiActiveContextNamesResponse extends AIResponse {

    private static final long serialVersionUID = 1L;

    public List names;
  }

  private static interface ApiActiveContextListResponse extends List {
  }

  private static class BadResponseStatusException extends Exception {

    private static final long serialVersionUID = 1L;

    private final AIResponse response;

    public BadResponseStatusException(final AIResponse response) {
      this.response = response;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy