/* * Copyright 2018 The Data Transfer Project Authors. * * 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 * * https://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.datatransferproject.transfer.rememberthemilk.tasks; import static com.google.common.base.Preconditions.checkState; import com.fasterxml.jackson.xml.XmlMapper; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.repackaged.com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.net.URL; import java.time.Instant; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.print.DocFlavor.STRING; import org.datatransferproject.transfer.rememberthemilk.model.tasks.GetListResponse; import org.datatransferproject.transfer.rememberthemilk.model.tasks.GetListsResponse; import org.datatransferproject.transfer.rememberthemilk.model.tasks.ListAddResponse; import org.datatransferproject.transfer.rememberthemilk.model.tasks.ListInfo; import org.datatransferproject.transfer.rememberthemilk.model.tasks.RememberTheMilkResponse; import org.datatransferproject.transfer.rememberthemilk.model.tasks.TaskUpdateResponse; import org.datatransferproject.transfer.rememberthemilk.model.tasks.TaskSeries; import org.datatransferproject.transfer.rememberthemilk.model.tasks.TimelineCreateResponse; class RememberTheMilkService { private static final String BASE_URL = "https://api.rememberthemilk.com/services/rest/"; private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); private RememberTheMilkSignatureGenerator signatureGenerator; private XmlMapper xmlMapper = new XmlMapper(); RememberTheMilkService(RememberTheMilkSignatureGenerator signatureGenerator) { this.signatureGenerator = signatureGenerator; } public String createTimeline() throws IOException { TimelineCreateResponse timelineCreateResponse = makeRequest( ImmutableMap.of("method", RememberTheMilkMethods.TIMELINES_CREATE.getMethodName()), TimelineCreateResponse.class); checkState(!Strings.isNullOrEmpty(timelineCreateResponse.timeline)); return timelineCreateResponse.timeline; } public ListInfo createTaskList(String name, String timeline) throws IOException { Map<String, String> params = ImmutableMap.of( "method", RememberTheMilkMethods.LISTS_ADD.getMethodName(), "timeline", timeline, "name", ("Copy of: " + name)); ListAddResponse response = makeRequest(params, ListAddResponse.class); checkState(response.list != null, "Added list is null"); checkState(response.list.id != 0, "Added list has id of zero"); return response.list; } public TaskSeries createTask(String name, String timeline, String listId) throws IOException { Map<String, String> params = ImmutableMap.of( "method", RememberTheMilkMethods.TASKS_ADD.getMethodName(), "timeline", timeline, "name", name, "list_id", listId); TaskUpdateResponse taskUpdateResponse = makeRequest(params, TaskUpdateResponse.class); return taskUpdateResponse.list.taskseries.get(0); } public void completeTask(String timeline, String listId, int seriesId, int taskId) throws IOException { // NB: The RTM API does not support setting an arbitrary completion time, so this method can // only mark a task as having been completed. Map<String, String> params = ImmutableMap.of( "method", RememberTheMilkMethods.TASKS_COMPLETE.getMethodName(), "timeline", timeline, "list_id", listId, "taskseries_id", String.valueOf(seriesId), "task_id", String.valueOf(taskId) ); makeRequest(params, TaskUpdateResponse.class); } public void setDueDate(String timeline, String listId, int seriesId, int taskId, Instant dueDate) throws IOException { // NB: does not set due time, merely due date // TODO: address due times Map<String, String> params = new LinkedHashMap<>(); params.put("method", RememberTheMilkMethods.TASKS_DUE_DATE.getMethodName()); params.put("timeline", timeline); params.put("list_id", listId); params.put("taskseries_id", String.valueOf(seriesId)); params.put("task_id", String.valueOf(taskId)); params.put("due", dueDate.toString()); makeRequest(params, TaskUpdateResponse.class); } public GetListResponse getList(String listId) throws IOException { Map<String, String> params = ImmutableMap.of( "method", RememberTheMilkMethods.TASKS_GET_LIST.getMethodName(), "list_id", listId); return makeRequest(params, GetListResponse.class); } public GetListsResponse getLists() throws IOException { return makeRequest( ImmutableMap.of("method", RememberTheMilkMethods.LISTS_GET_LIST.getMethodName()), GetListsResponse.class); } private <T extends RememberTheMilkResponse> T makeRequest( Map<String, String> parameters, Class<T> dataClass) throws IOException { URL signedUrl = signatureGenerator.getSignature(BASE_URL, parameters); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(); HttpRequest getRequest = requestFactory.buildGetRequest(new GenericUrl(signedUrl)); HttpResponse response = getRequest.execute(); int statusCode = response.getStatusCode(); if (statusCode != 200) { throw new IOException( "Bad status code: " + statusCode + " error: " + response.getStatusMessage()); } T parsedResponse = xmlMapper.readValue(response.getContent(), dataClass); if (parsedResponse.error != null) { throw new IOException( "Error making call to " + signedUrl + " error: " + parsedResponse.error); } return parsedResponse; } private enum RememberTheMilkMethods { LISTS_GET_LIST("rtm.lists.getList"), LISTS_ADD("rtm.lists.add"), TASKS_ADD("rtm.tasks.add"), TASKS_COMPLETE("rtm.tasks.complete"), TASKS_DUE_DATE("rtm.tasks.setDueDate"), TASKS_GET_LIST("rtm.tasks.getList"), TIMELINES_CREATE("rtm.timelines.create"); private final String methodName; RememberTheMilkMethods(String methodName) { this.methodName = methodName; } String getMethodName() { return methodName; } } }