Package com.almende.eve.agent.google

Source Code of com.almende.eve.agent.google.GoogleTaskAgent

package com.almende.eve.agent.google;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.joda.time.DateTime;

import com.almende.eve.agent.Agent;
import com.almende.eve.agent.TaskAgent;
import com.almende.eve.agent.annotation.Name;
import com.almende.eve.agent.annotation.Required;
import com.almende.eve.config.Config;
import com.almende.eve.entity.calendar.Authorization;
import com.almende.eve.rpc.jsonrpc.JSONRPCException;
import com.almende.eve.rpc.jsonrpc.JSONRPCException.CODE;
import com.almende.eve.rpc.jsonrpc.jackson.JOM;
import com.almende.eve.state.State;
import com.almende.util.HttpUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class GoogleTaskAgent extends Agent implements TaskAgent {
  private Logger logger = Logger.getLogger(this.getClass().getName());
 
  private String OAUTH_URI = "https://accounts.google.com/o/oauth2";
  private String CALENDAR_URI = "https://www.googleapis.com/tasks/v1/";

  /**
   * Set access token and refresh token, used to authorize the calendar agent.
   * These tokens must be retrieved via Oauth 2.0 authorization.
   * @param access_token
   * @param token_type
   * @param expires_in
   * @param refresh_token
   * @throws IOException
   */
  public void setAuthorization (
      @Name("access_token") String access_token,
      @Name("token_type") String token_type,
      @Name("expires_in") Integer expires_in,
      @Name("refresh_token") String refresh_token) throws IOException {
    logger.info("setAuthorization");
   
    State state = getState();
   
    // retrieve user information
    String url = "https://www.googleapis.com/oauth2/v1/userinfo";
    Map<String, String> headers = new HashMap<String, String>();
    headers.put("Authorization", token_type + " " + access_token);
    String resp = HttpUtil.get(url, headers);
   
    ObjectNode info = JOM.getInstance().readValue(resp, ObjectNode.class);
    String email = info.has("email") ? info.get("email").asText() : null;
    String name = info.has("name") ? info.get("name").asText() : null;
   
    DateTime expires_at = calculateExpiresAt(expires_in);
    Authorization auth = new Authorization(access_token, token_type,
        expires_at, refresh_token);
   
    // store the tokens in the state
    state.put("auth", auth);
    state.put("email", email);
    state.put("name", name);
  }
 
  /**
   * Calculate the expiration time from a life time
   * @param expires_in      Expiration time in seconds
   * @return
   */
  private DateTime calculateExpiresAt(Integer expires_in) {
    DateTime expires_at = null;
    if (expires_in != null && expires_in != 0) {
      // calculate expiration time, and subtract 5 minutes for safety
      expires_at = DateTime.now().plusSeconds(expires_in).minusMinutes(5);
    }
    return expires_at;
  }
   
  /**
   * Refresh the access token using the refresh token
   * the tokens in provided authorization object will be updated
   * @param auth
   * @throws Exception
   */
  private void refreshAuthorization (Authorization auth) throws Exception {
    String refresh_token = (auth != null) ? auth.getRefreshToken() : null;
    if (refresh_token == null) {
      throw new Exception("No refresh token available");
    }
   
    Config config = getAgentFactory().getConfig();
    String client_id = config.get("google", "client_id");
    String client_secret = config.get("google", "client_secret");
   
    // retrieve new access_token using the refresh_token
    Map<String, String> params = new HashMap<String, String>();
    params.put("client_id", client_id);
    params.put("client_secret", client_secret);
    params.put("refresh_token", refresh_token);
    params.put("grant_type", "refresh_token");
    String resp = HttpUtil.postForm(OAUTH_URI + "/token", params);
    ObjectNode json = JOM.getInstance().readValue(resp, ObjectNode.class);
    if (!json.has("access_token")) {
      // TODO: give more specific error message
      throw new Exception("Retrieving new access token failed");
    }
   
    // update authorization
    if (json.has("access_token")) {
      auth.setAccessToken(json.get("access_token").asText());
    }
    if (json.has("expires_in")) {
      Integer expires_in = json.get("expires_in").asInt();
      DateTime expires_at = calculateExpiresAt(expires_in);
      auth.setExpiresAt(expires_at);
    }
  }
 
  /**
   * Get ready-made HTTP request headers containing the authorization token
   * Example usage: HttpUtil.get(url, getAuthorizationHeaders());
   * @return
   * @throws Exception
   */
  private Map<String, String> getAuthorizationHeaders () throws Exception {
    Authorization auth = getAuthorization();
   
    String access_token = (auth != null) ? auth.getAccessToken() : null;
    if (access_token == null) {
      throw new Exception("No authorization token available");
    }
    String token_type = (auth != null) ? auth.getTokenType() : null;
    if (token_type == null) {
      throw new Exception("No token type available");
    }
   
    Map<String, String> headers = new HashMap<String, String>();
    headers.put("Authorization", token_type + " " + access_token);
    return headers;
  }
 
  /**
   * Retrieve authorization tokens
   * @return
   * @throws Exception
   */
  private Authorization getAuthorization() throws Exception {
    Authorization auth = (Authorization) getState().get("auth");

    // check if access_token is expired
    DateTime expires_at = (auth != null) ? auth.getExpiresAt() : null;
    if (expires_at != null && expires_at.isBeforeNow()) {
      // TODO: remove this logging
      logger.info("access token is expired. refreshing now...");
      refreshAuthorization(auth);
      getState().put("auth", auth);
    }
   
    return auth;
  }
 
  /**
   * Get the calendar agents version
   */
  @Override
  public String getDescription() {
    return "This agent gives access to a Google Tasks. " +
        "It allows to search tasks " +
        "and add, edit, or remove tasks.";
  }

  /**
   * Get the calendar agents description
   */
  @Override
  public String getVersion() {
    return "0.1";
  }
 
  /**
   * Get the username associated with the tasks
   * @return name
   */
  @Override
  public String getUsername() {
    return (String) getState().get("name");
  }

  /**
   * Get the email associated with the tasks
   * @return name
   */
  @Override
  public String getEmail() {
    return (String) getState().get("email");
  }
 
  /**
   * Retrieve a list with all calendars in this google calendar
   * @return  String with id of the task list
   */
  private String getDefaultTaskList() throws Exception {
   
    String defaultList=(String) getState().get("defaultList");
    if(defaultList==null) {
      ArrayNode taskLists = getTaskList();
      for(JsonNode taskList : taskLists) {
        if(taskList.get("title").textValue().equals("Paige Task List")){
          getState().put("defaultList", taskList.get("id").textValue());
          return taskList.get("id").textValue();
        }
      }
    }
   
    // No default list found so going to create one
    if(defaultList==null) {
      ObjectNode taskList = JOM.createObjectNode();
      taskList.put("title", "Paige Task List");
      taskList = createTaskList(taskList);
      defaultList = taskList.get("id").textValue();
      getState().put("defaultList", defaultList);
    }
   
    return defaultList;
  }
 
  /**
   * Create a task list
   * @param taskList    JSON structure containing a taskList
   * @return        JSON sturcture with created tasklist
   */
  public ObjectNode createTaskList(@Name("taskList") ObjectNode taskList)
      throws Exception {
   
    String url = CALENDAR_URI + "users/@me/lists";
   
    // perform POST request
    ObjectMapper mapper = JOM.getInstance();
    String body = mapper.writeValueAsString(taskList);
    Map<String, String> headers = getAuthorizationHeaders();
    headers.put("Content-Type", "application/json");
    String resp = HttpUtil.post(url, body, headers);
    ObjectNode createdTaskList = mapper.readValue(resp, ObjectNode.class);
       
    // check for errors
    if (createdTaskList.has("error")) {
      ObjectNode error = (ObjectNode)createdTaskList.get("error");
      throw new JSONRPCException(error);
    }
   
    logger.info("createTaskList=" + JOM.getInstance().writeValueAsString(createdTaskList));

    return createdTaskList;
  }

  /**
   *   Retrieve a list with all task lists in this google tasks
   */
  @Override
  public ArrayNode getTaskList() throws Exception {
    String url = CALENDAR_URI + "users/@me/lists";
    String resp = HttpUtil.get(url, getAuthorizationHeaders());
    ObjectNode calendars = JOM.getInstance().readValue(resp, ObjectNode.class);

    // check for errors
    if (calendars.has("error")) {
      ObjectNode error = (ObjectNode)calendars.get("error");
      throw new JSONRPCException(error);
    }

    // get items from response
    ArrayNode items = null;
    if (calendars.has("items")) {
      items = (ArrayNode)calendars.get("items");
    }
    else {
      items = JOM.createArrayNode();
    }
   
    return items;
  }

  /**
   * Retrieve a list of task on a certain task list
   * @param dueMin         Minimal due time (optional)
   * @param dueMax         Maximal due time (optional)
   * @param calendarId      Optional task listid. the default task list is
   *                         used by default
   */
  @Override
  public ArrayNode getTasks(@Required(false) @Name("dueMin") String dueMin,
                @Required(false) @Name("dueMax") String dueMax,
                @Required(false) @Name("taskListId") String taskListId)
      throws Exception {
    if (taskListId == null) {
      taskListId = getDefaultTaskList();
    }
   
    if (taskListId == null) {
      throw new Exception("No tasklist given and no default list found");
    }
    // built url with query parameters
    String url = CALENDAR_URI + "lists/" + taskListId + "/tasks";
    Map<String, String> params = new HashMap<String, String>();
    if (dueMin != null) {
      params.put("dueMin", new DateTime(dueMin).toString());
    }
    if (dueMax != null) {
      params.put("dueMax", new DateTime(dueMax).toString());
    }
    // Set singleEvents=true to expand recurring events into instances
    //params.put("singleEvents", "true");
    url = HttpUtil.appendQueryParams(url, params);
   
    // perform GET request
    Map<String, String> headers = getAuthorizationHeaders();
    String resp = HttpUtil.get(url, headers);
    ObjectMapper mapper = JOM.getInstance();
    ObjectNode json = mapper.readValue(resp, ObjectNode.class);
   
    // check for errors
    if (json.has("error")) {
      ObjectNode error = (ObjectNode)json.get("error");
      throw new JSONRPCException(error);
    }

    // get items from the response
    ArrayNode items = null;
    if (json.has("items")){
      items = (ArrayNode) json.get("items");
     
      /* TODO: cleanup?
      // convert from Google to Eve event
      for (int i = 0; i < items.size(); i++) {
        ObjectNode item = (ObjectNode) items.get(i);
        toEveEvent(item);
      }
      */
    }
    else {
      items = JOM.createArrayNode();
    }
   
    return items;
  }

  /**
   * Get a single task by id
   * @param taskId         Id of the task
   * @param taksListId      Optional task listid. the default task list is
   *                         used by default
   */
  @Override
  public ObjectNode getTask(@Name("taskId") String taskId,
                @Required(false) @Name("taskListId") String taskListId)
      throws Exception {
    if (taskListId == null) {
      taskListId = getDefaultTaskList();
    }

    // built url
    String url = CALENDAR_URI + "lists/" + taskListId + "/tasks/" + taskId;
   
    // perform GET request
    Map<String, String> headers = getAuthorizationHeaders();
    String resp = HttpUtil.get(url, headers);
    ObjectMapper mapper = JOM.getInstance();
    ObjectNode task = mapper.readValue(resp, ObjectNode.class);
   
    logger.info("getTask task=" + (task != null ?
        JOM.getInstance().writeValueAsString(task) : null));
   
    // check for errors
    if (task.has("error")) {
      ObjectNode error = (ObjectNode)task.get("error");
      Integer code = error.has("code") ? error.get("code").asInt() : null;
      if (code != null && code.equals(404)) {
        throw new JSONRPCException(CODE.NOT_FOUND);       
      }
     
      throw new JSONRPCException(error);
    }
   
    // check if canceled. If so, return null
    // TODO: be able to retrieve canceled events?
    if (task.has("status") && task.get("status").asText().equals("cancelled")) {
      throw new JSONRPCException(CODE.NOT_FOUND);
    }
   
    return task;
  }

  /**
   * Create a task
   * @param task           JSON structure containing the task
   * @param taksListId      Optional task listid. the default task list is
   *                         used by default
   * @return createdTask   JSON structure with the created task
   */
  @Override
  public ObjectNode createTask(@Name("task") ObjectNode task,
                 @Required(false) @Name("taskListId") String taskListId)
      throws Exception {
    if (taskListId == null) {
      taskListId = getDefaultTaskList();
    }

    // built url
    String url = CALENDAR_URI + "lists/" + taskListId + "/tasks";
   
    // perform POST request
    ObjectMapper mapper = JOM.getInstance();
    String body = mapper.writeValueAsString(task);
    Map<String, String> headers = getAuthorizationHeaders();
    headers.put("Content-Type", "application/json");
    String resp = HttpUtil.post(url, body, headers);
    ObjectNode createdTask = mapper.readValue(resp, ObjectNode.class);
       
    // check for errors
    if (createdTask.has("error")) {
      ObjectNode error = (ObjectNode)createdTask.get("error");
      throw new JSONRPCException(error);
    }
   
    logger.info("createTask=" + JOM.getInstance().writeValueAsString(createdTask));

    return createdTask;
  }

  /**
   * Update an existing task
   * @param task           JSON structure containing the task
   *                         (task must have an id)
   * @param taksListId      Optional task listid. the default task list is
   *                         used by default
   * @return updatedTask   JSON structure with the updated task
   */
  @Override
  public ObjectNode updateTask(@Name("task") ObjectNode task,
                 @Required(false) @Name("taskListId")String taskListId)
      throws Exception {
    if (taskListId == null) {
      taskListId = getDefaultTaskList();
    }
   
    // read id from event
    String id = task.get("id").asText();
    if (id == null) {
      throw new Exception("Parameter 'id' missing in task");
    }
   
    // built url
    String url = CALENDAR_URI + "lists/" + taskListId + "/tasks/" + id;
   
    // perform POST request
    ObjectMapper mapper = JOM.getInstance();
    String body = mapper.writeValueAsString(task);
    Map<String, String> headers = getAuthorizationHeaders();
    headers.put("Content-Type", "application/json");
    String resp = HttpUtil.put(url, body, headers);
    ObjectNode updatedTask = mapper.readValue(resp, ObjectNode.class);
   
    // check for errors
    if (updatedTask.has("error")) {
      ObjectNode error = (ObjectNode)updatedTask.get("error");
      throw new JSONRPCException(error);
    }
   
    logger.info("updateTask=" + JOM.getInstance().writeValueAsString(updatedTask)); // TODO: cleanup

    return updatedTask;
  }

  /**
   * Delete an existing task
   * @param taskId         id of the task to be deleted
   * @param taksListId      Optional task listid. the default task list is
   *                         used by default
   */
  @Override
  public void deleteTask(@Name("taskId") String taskId,
               @Required(false) @Name("taskListId") String taskListId) throws Exception {
    if (taskListId == null) {
      taskListId = getDefaultTaskList();
    }
   
    logger.info("deleteTask taskId=" + taskId + ", taskListId=" + taskListId); // TODO: cleanup

    // built url
    String url = CALENDAR_URI + "lists/" + taskListId + "/tasks/" + taskId;
   
    // perform POST request
    Map<String, String> headers = getAuthorizationHeaders();
    String resp = HttpUtil.delete(url, headers);
    if (!resp.isEmpty()) {
      throw new Exception(resp);
    }
  }
 
  /**
   * Remove all stored data from this agent
   */
  public void clear() {
    State state = getState();
    state.remove("auth");
    state.remove("email");
    state.remove("name");
    state.remove("defaultList");
  }

}
TOP

Related Classes of com.almende.eve.agent.google.GoogleTaskAgent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.