{"action":"created","comment":{"url":"https://api.github.com/repos/dmfs/opentasks/pulls/comments/139463111","pull_request_review_id":63399091,"id":139463111,"diff_hunk":"@@ -0,0 +1,404 @@\n+/*\n+ * Copyright 2017 dmfs GmbH\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+package org.dmfs.provider.tasks;\n+\n+import android.content.ContentProviderClient;\n+import android.content.ContentResolver;\n+import android.content.ContentValues;\n+import android.content.Context;\n+import android.os.Build;\n+import android.support.test.InstrumentationRegistry;\n+import android.support.test.runner.AndroidJUnit4;\n+\n+import org.dmfs.android.contentpal.OperationsQueue;\n+import org.dmfs.android.contentpal.RowSnapshot;\n+import org.dmfs.android.contentpal.Table;\n+import org.dmfs.android.contentpal.batches.MultiBatch;\n+import org.dmfs.android.contentpal.batches.SingletonBatch;\n+import org.dmfs.android.contentpal.operations.Assert;\n+import org.dmfs.android.contentpal.operations.BulkDelete;\n+import org.dmfs.android.contentpal.operations.Delete;\n+import org.dmfs.android.contentpal.operations.Put;\n+import org.dmfs.android.contentpal.operations.Related;\n+import org.dmfs.android.contentpal.predicates.AllOf;\n+import org.dmfs.android.contentpal.predicates.EqArg;\n+import org.dmfs.android.contentpal.queues.BasicOperationsQueue;\n+import org.dmfs.android.contentpal.rowdata.Composite;\n+import org.dmfs.android.contentpal.rowdata.EmptyRowData;\n+import org.dmfs.android.contentpal.rowsnapshots.VirtualRowSnapshot;\n+import org.dmfs.android.contenttestpal.operations.AssertEmptyTable;\n+import org.dmfs.opentaskspal.tables.InstanceTable;\n+import org.dmfs.opentaskspal.tables.LocalTaskListsTable;\n+import org.dmfs.opentaskspal.tables.TaskListScoped;\n+import org.dmfs.opentaskspal.tables.TaskListsTable;\n+import org.dmfs.opentaskspal.tables.TasksTable;\n+import org.dmfs.opentaskspal.tasklists.NameData;\n+import org.dmfs.opentaskspal.tasks.OriginalInstanceSyncIdData;\n+import org.dmfs.opentaskspal.tasks.SyncIdData;\n+import org.dmfs.opentaskspal.tasks.TimeData;\n+import org.dmfs.opentaskspal.tasks.TitleData;\n+import org.dmfs.rfc5545.DateTime;\n+import org.dmfs.rfc5545.Duration;\n+import org.dmfs.tasks.contract.TaskContract.Instances;\n+import org.dmfs.tasks.contract.TaskContract.TaskLists;\n+import org.dmfs.tasks.contract.TaskContract.Tasks;\n+import org.junit.After;\n+import org.junit.Before;\n+import org.junit.Test;\n+import org.junit.runner.RunWith;\n+\n+import java.util.TimeZone;\n+\n+import static org.dmfs.android.contenttestpal.ContentMatcher.resultsIn;\n+import static org.junit.Assert.assertThat;\n+\n+\n+/**\n+ * Tests for {@link TaskProvider}.\n+ *\n+ * @author Yannic Ahrens\n+ * @author Gabor Keszthelyi\n+ */\n+@RunWith(AndroidJUnit4.class)\n+public class TaskProviderTest\n+{\n+    private ContentResolver mResolver;\n+    private String mAuthority;\n+    private Context mContext;\n+    private ContentProviderClient mClient;\n+\n+\n+    @Before\n+    public void setUp() throws Exception\n+    {\n+        mContext = InstrumentationRegistry.getTargetContext();\n+        mResolver = mContext.getContentResolver();\n+        mAuthority = AuthorityUtil.taskAuthority(mContext);\n+        mClient = mContext.getContentResolver().acquireContentProviderClient(mAuthority);\n+\n+        // Assert that tables are empty:\n+        OperationsQueue queue = new BasicOperationsQueue(mClient);\n+        queue.enqueue(new MultiBatch(\n+                new AssertEmptyTable<>(new TasksTable(mAuthority)),\n+                new AssertEmptyTable<>(new TaskListsTable(mAuthority)),\n+                new AssertEmptyTable<>(new InstanceTable(mAuthority))));\n+        queue.flush();\n+    }\n+\n+\n+    @After\n+    public void tearDown() throws Exception\n+    {\n+        /*\n+        TODO When Test Orchestration is available, there will be no need for clear up, every test will run in separate instrumentation\n+        https://android-developers.googleblog.com/2017/07/android-testing-support-library-10-is.html\n+        https://developer.android.com/training/testing/junit-runner.html#using-android-test-orchestrator\n+        */\n+\n+        // Clear the DB:\n+        BasicOperationsQueue queue = new BasicOperationsQueue(mClient);\n+        queue.enqueue(new SingletonBatch(new BulkDelete<>(new LocalTaskListsTable(mAuthority))));\n+        queue.flush();\n+\n+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)\n+        {\n+            mClient.close();\n+        }\n+        else\n+        {\n+            mClient.release();\n+        }\n+    }\n+\n+\n+    @Test\n+    public void testSingleInsert() throws Exception\n+    {\n+        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));\n+        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TaskListScoped(taskList, new TasksTable(mAuthority)));\n+\n+        assertThat(new MultiBatch(\n+                new Put<>(taskList, new NameData(\"list1\")),\n+                new Put<>(task, new TitleData(\"task1\"))\n+\n+        ), resultsIn(mClient,\n+                new Assert<>(taskList, new NameData(\"list1\")),\n+                new Assert<>(task, new TitleData(\"task1\")),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task, Instances.TASK_ID)\n+        ));\n+    }\n+\n+\n+    /**\n+     * Creates three new Tasks. One of them will refer to a different TasksList.\n+     */\n+    @Test\n+    public void testMultipleInserts()\n+    {\n+        Table<TaskLists> taskListsTable = new LocalTaskListsTable(mAuthority);\n+        RowSnapshot<TaskLists> taskList1 = new VirtualRowSnapshot<>(taskListsTable);\n+        RowSnapshot<TaskLists> taskList2 = new VirtualRowSnapshot<>(taskListsTable);\n+        RowSnapshot<Tasks> task1 = new VirtualRowSnapshot<>(new TaskListScoped(taskList1, new TasksTable(mAuthority)));\n+        RowSnapshot<Tasks> task2 = new VirtualRowSnapshot<>(new TaskListScoped(taskList1, new TasksTable(mAuthority)));\n+        RowSnapshot<Tasks> task3 = new VirtualRowSnapshot<>(new TaskListScoped(taskList2, new TasksTable(mAuthority)));\n+\n+        assertThat(new MultiBatch(\n+                new Put<>(taskList1, new NameData(\"list1\")),\n+                new Put<>(taskList2, new NameData(\"list2\")),\n+                new Put<>(task1, new TitleData(\"task1\")),\n+                new Put<>(task2, new TitleData(\"task2\")),\n+                new Put<>(task3, new TitleData(\"task3\"))\n+\n+        ), resultsIn(mClient,\n+                new Assert<>(taskList1, new NameData(\"list1\")),\n+                new Assert<>(taskList2, new NameData(\"list2\")),\n+                new Assert<>(task1, new TitleData(\"task1\")),\n+                new Assert<>(task2, new TitleData(\"task2\")),\n+                new Assert<>(task3, new TitleData(\"task3\")),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task1, Instances.TASK_ID),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task2, Instances.TASK_ID),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task3, Instances.TASK_ID)\n+        ));\n+    }\n+\n+\n+    @Test\n+    public void testInsertTaskWithStartAndDue()\n+    {\n+        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));\n+        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TaskListScoped(taskList, new TasksTable(mAuthority)));\n+\n+        DateTime start = DateTime.now();\n+        DateTime due = start.addDuration(new Duration(1, 1, 0));\n+\n+        assertThat(new MultiBatch(\n+                new Put<>(taskList, new EmptyRowData<TaskLists>()),\n+                new Put<>(task, new TimeData(start, due))\n+\n+        ), resultsIn(mClient,\n+                new Assert<>(task, new TimeData(start, due)),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task, Instances.TASK_ID, new AllOf(\n+                        new EqArg(Instances.INSTANCE_START, start.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DUE, due.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DURATION, due.getTimestamp() - start.getTimestamp()),\n+                        new EqArg(Tasks.TZ, start.isAllDay() ? \"UTC\" : start.getTimeZone().getID())\n+                ))\n+        ));\n+    }\n+\n+\n+    @Test\n+    public void testInsertWithStartAndDuration()\n+    {\n+        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));\n+        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TaskListScoped(taskList, new TasksTable(mAuthority)));\n+\n+        DateTime start = DateTime.now();\n+        String durationStr = \"PT1H\";\n+        Duration duration = Duration.parse(durationStr);\n+        long durationMillis = duration.toMillis();\n+        TimeZone timeZone = TimeZone.getDefault();\n+\n+        assertThat(new MultiBatch(\n+                new Put<>(taskList, new EmptyRowData<TaskLists>()),\n+                new Put<>(task, new TimeData(start, durationStr))\n+\n+        ), resultsIn(mClient,\n+                new Assert<>(task, new TimeData(start, durationStr)),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task, Instances.TASK_ID, new AllOf(\n+                        new EqArg(Instances.INSTANCE_START, start.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DUE, start.addDuration(duration).getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DURATION, durationMillis),\n+                        new EqArg(Tasks.TZ, \"UTC\")\n+                ))\n+        ));\n+    }\n+\n+\n+    /**\n+     * Having a table with a single task. Update task and check if instance updates accordingly.\n+     */\n+    @Test\n+    public void testUpdateDue()\n+    {\n+        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));\n+        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TaskListScoped(taskList, new TasksTable(mAuthority)));\n+        OperationsQueue queue = new BasicOperationsQueue(mClient);\n+\n+        DateTime start = DateTime.now();\n+        DateTime due = start.addDuration(new Duration(1, 0, 1));\n+        TimeZone timeZone = TimeZone.getDefault();\n+\n+        assertThat(new MultiBatch(\n+                new Put<>(taskList, new NameData(\"list1\")),\n+                new Put<>(task, new TimeData(start, due))\n+\n+        ), resultsIn(queue,\n+                new Assert<>(task, new TimeData(start, due)),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task, Instances.TASK_ID, new AllOf(\n+                        new EqArg(Instances.INSTANCE_START, start.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DUE, due.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DURATION, due.getTimestamp() - start.getTimestamp()),\n+                        new EqArg(Tasks.TZ, \"UTC\")\n+                ))\n+        ));\n+\n+        DateTime due2 = due.addDuration(new Duration(1, 0, 2));\n+\n+        assertThat(new SingletonBatch(\n+                new Put<>(task, new TimeData(start, due2))\n+\n+        ), resultsIn(queue,\n+                new Assert<>(task, new TimeData(start, due2)),\n+                new PostOperationRelatedAssert<>(new InstanceTable(mAuthority), task, Instances.TASK_ID, new AllOf(\n+                        new EqArg(Instances.INSTANCE_START, start.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DUE, due2.getTimestamp()),\n+                        new EqArg(Instances.INSTANCE_DURATION, due2.getTimestamp() - start.getTimestamp()),\n+                        new EqArg(Tasks.TZ, \"UTC\")\n+                ))\n+        ));\n+    }\n+\n+\n+    /**\n+     * Having a table with a single task. Delete task and check if instance is deleted accordingly.\n+     */\n+    @Test\n+    public void testInstanceDelete()\n+    {\n+        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));\n+        Table<Tasks> taskTable = new TaskListScoped(taskList, new TasksTable(mAuthority));\n+        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(taskTable);\n+        OperationsQueue queue = new BasicOperationsQueue(mClient);\n+\n+        assertThat(new MultiBatch(","path":"opentasks-provider/src/androidTest/java/org/dmfs/provider/tasks/TaskProviderTest.java","position":null,"original_position":288,"commit_id":"fbbbcab95ee0b63cc82eb22c04131210b1f87f3e","original_commit_id":"49d553cd097577fcde048345a2436645bc56d69c","user":{"login":"lemonboston","id":1077972,"avatar_url":"https://avatars0.githubusercontent.com/u/1077972?v=4","gravatar_id":"","url":"https://api.github.com/users/lemonboston","html_url":"https://github.com/lemonboston","followers_url":"https://api.github.com/users/lemonboston/followers","following_url":"https://api.github.com/users/lemonboston/following{/other_user}","gists_url":"https://api.github.com/users/lemonboston/gists{/gist_id}","starred_url":"https://api.github.com/users/lemonboston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lemonboston/subscriptions","organizations_url":"https://api.github.com/users/lemonboston/orgs","repos_url":"https://api.github.com/users/lemonboston/repos","events_url":"https://api.github.com/users/lemonboston/events{/privacy}","received_events_url":"https://api.github.com/users/lemonboston/received_events","type":"User","site_admin":false},"body":"Removed that unnecessary assert.","created_at":"2017-09-18T15:53:28Z","updated_at":"2017-09-18T15:53:28Z","html_url":"https://github.com/dmfs/opentasks/pull/418#discussion_r139463111","pull_request_url":"https://api.github.com/repos/dmfs/opentasks/pulls/418","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/dmfs/opentasks/pulls/comments/139463111"},"html":{"href":"https://github.com/dmfs/opentasks/pull/418#discussion_r139463111"},"pull_request":{"href":"https://api.github.com/repos/dmfs/opentasks/pulls/418"}},"in_reply_to_id":139360895},"pull_request":{"url":"https://api.github.com/repos/dmfs/opentasks/pulls/418","id":140615648,"html_url":"https://github.com/dmfs/opentasks/pull/418","diff_url":"https://github.com/dmfs/opentasks/pull/418.diff","patch_url":"https://github.com/dmfs/opentasks/pull/418.patch","issue_url":"https://api.github.com/repos/dmfs/opentasks/issues/418","number":418,"state":"open","locked":false,"title":"Tests for TaskProvider #410","user":{"login":"lemonboston","id":1077972,"avatar_url":"https://avatars0.githubusercontent.com/u/1077972?v=4","gravatar_id":"","url":"https://api.github.com/users/lemonboston","html_url":"https://github.com/lemonboston","followers_url":"https://api.github.com/users/lemonboston/followers","following_url":"https://api.github.com/users/lemonboston/following{/other_user}","gists_url":"https://api.github.com/users/lemonboston/gists{/gist_id}","starred_url":"https://api.github.com/users/lemonboston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lemonboston/subscriptions","organizations_url":"https://api.github.com/users/lemonboston/orgs","repos_url":"https://api.github.com/users/lemonboston/repos","events_url":"https://api.github.com/users/lemonboston/events{/privacy}","received_events_url":"https://api.github.com/users/lemonboston/received_events","type":"User","site_admin":false},"body":"Remove dynamic authority resolution in AuthorityUtil.\r\n\r\n---\r\n\r\nSubmitting this now for first check. There are many TODOs left in code.\r\n\r\nA few specific notes:\r\n- opentaskpal classes are not reviewed yet\r\n- the contentpal test support ended up being one simple `Matcher` class basically, but I used this `Check` type to implement it and make an adapter for `Matcher`. I haven't thought it through yet since things got much more simple with the `Assert` operations. One thing is that `Matcher` seems not to be completely straighforward for me with the 3 base classes, also I think what we do here is more an execution which can throw Exception than a simple comparison of an already calculated value, so that's why this interface may be better.\r\nWe could also forget the hamcrest assert as well, I suppose, to write something like:\r\n```\r\nexecute(batch).with(client).andVerifyContent(assertOperations)\r\n```\r\nwith our own types.\r\nImplementation may depend a little bit on the eager queue stuff as well.\r\n- not sure where the convenience `Assert*` composed operations should go, to contentpal module or contentpal-testing module\r\n- I've left `AA_TO_*` package names to indicate which module the sibling packages are intended to end up in\r\n- There is one bigger test case left in original form, I will update that, too, so the related private methods can be removed as well\r\n- All test cases are based on the original ones from the old code\r\n- There are possible ways to improve the test code usage, there are some issues to be solved as well (TODOs in code)\r\n- `Assert` for `RowSnapshot` is not available yet, so that will result in changes as well\r\n\r\nThis is a single commit now, I will continue to commit on top of it.","created_at":"2017-09-12T15:39:21Z","updated_at":"2017-09-18T15:53:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"55372d062143e569de4b877d60e12cde5a925d74","assignee":{"login":"lemonboston","id":1077972,"avatar_url":"https://avatars0.githubusercontent.com/u/1077972?v=4","gravatar_id":"","url":"https://api.github.com/users/lemonboston","html_url":"https://github.com/lemonboston","followers_url":"https://api.github.com/users/lemonboston/followers","following_url":"https://api.github.com/users/lemonboston/following{/other_user}","gists_url":"https://api.github.com/users/lemonboston/gists{/gist_id}","starred_url":"https://api.github.com/users/lemonboston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lemonboston/subscriptions","organizations_url":"https://api.github.com/users/lemonboston/orgs","repos_url":"https://api.github.com/users/lemonboston/repos","events_url":"https://api.github.com/users/lemonboston/events{/privacy}","received_events_url":"https://api.github.com/users/lemonboston/received_events","type":"User","site_admin":false},"assignees":[{"login":"lemonboston","id":1077972,"avatar_url":"https://avatars0.githubusercontent.com/u/1077972?v=4","gravatar_id":"","url":"https://api.github.com/users/lemonboston","html_url":"https://github.com/lemonboston","followers_url":"https://api.github.com/users/lemonboston/followers","following_url":"https://api.github.com/users/lemonboston/following{/other_user}","gists_url":"https://api.github.com/users/lemonboston/gists{/gist_id}","starred_url":"https://api.github.com/users/lemonboston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lemonboston/subscriptions","organizations_url":"https://api.github.com/users/lemonboston/orgs","repos_url":"https://api.github.com/users/lemonboston/repos","events_url":"https://api.github.com/users/lemonboston/events{/privacy}","received_events_url":"https://api.github.com/users/lemonboston/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/dmfs/opentasks/pulls/418/commits","review_comments_url":"https://api.github.com/repos/dmfs/opentasks/pulls/418/comments","review_comment_url":"https://api.github.com/repos/dmfs/opentasks/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dmfs/opentasks/issues/418/comments","statuses_url":"https://api.github.com/repos/dmfs/opentasks/statuses/fbbbcab95ee0b63cc82eb22c04131210b1f87f3e","head":{"label":"dmfs:mx/410-provider-tests","ref":"mx/410-provider-tests","sha":"fbbbcab95ee0b63cc82eb22c04131210b1f87f3e","user":{"login":"dmfs","id":1614987,"avatar_url":"https://avatars3.githubusercontent.com/u/1614987?v=4","gravatar_id":"","url":"https://api.github.com/users/dmfs","html_url":"https://github.com/dmfs","followers_url":"https://api.github.com/users/dmfs/followers","following_url":"https://api.github.com/users/dmfs/following{/other_user}","gists_url":"https://api.github.com/users/dmfs/gists{/gist_id}","starred_url":"https://api.github.com/users/dmfs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dmfs/subscriptions","organizations_url":"https://api.github.com/users/dmfs/orgs","repos_url":"https://api.github.com/users/dmfs/repos","events_url":"https://api.github.com/users/dmfs/events{/privacy}","received_events_url":"https://api.github.com/users/dmfs/received_events","type":"User","site_admin":false},"repo":{"id":10550221,"name":"opentasks","full_name":"dmfs/opentasks","owner":{"login":"dmfs","id":1614987,"avatar_url":"https://avatars3.githubusercontent.com/u/1614987?v=4","gravatar_id":"","url":"https://api.github.com/users/dmfs","html_url":"https://github.com/dmfs","followers_url":"https://api.github.com/users/dmfs/followers","following_url":"https://api.github.com/users/dmfs/following{/other_user}","gists_url":"https://api.github.com/users/dmfs/gists{/gist_id}","starred_url":"https://api.github.com/users/dmfs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dmfs/subscriptions","organizations_url":"https://api.github.com/users/dmfs/orgs","repos_url":"https://api.github.com/users/dmfs/repos","events_url":"https://api.github.com/users/dmfs/events{/privacy}","received_events_url":"https://api.github.com/users/dmfs/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dmfs/opentasks","description":"A task app for Android","fork":false,"url":"https://api.github.com/repos/dmfs/opentasks","forks_url":"https://api.github.com/repos/dmfs/opentasks/forks","keys_url":"https://api.github.com/repos/dmfs/opentasks/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dmfs/opentasks/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dmfs/opentasks/teams","hooks_url":"https://api.github.com/repos/dmfs/opentasks/hooks","issue_events_url":"https://api.github.com/repos/dmfs/opentasks/issues/events{/number}","events_url":"https://api.github.com/repos/dmfs/opentasks/events","assignees_url":"https://api.github.com/repos/dmfs/opentasks/assignees{/user}","branches_url":"https://api.github.com/repos/dmfs/opentasks/branches{/branch}","tags_url":"https://api.github.com/repos/dmfs/opentasks/tags","blobs_url":"https://api.github.com/repos/dmfs/opentasks/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dmfs/opentasks/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dmfs/opentasks/git/refs{/sha}","trees_url":"https://api.github.com/repos/dmfs/opentasks/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dmfs/opentasks/statuses/{sha}","languages_url":"https://api.github.com/repos/dmfs/opentasks/languages","stargazers_url":"https://api.github.com/repos/dmfs/opentasks/stargazers","contributors_url":"https://api.github.com/repos/dmfs/opentasks/contributors","subscribers_url":"https://api.github.com/repos/dmfs/opentasks/subscribers","subscription_url":"https://api.github.com/repos/dmfs/opentasks/subscription","commits_url":"https://api.github.com/repos/dmfs/opentasks/commits{/sha}","git_commits_url":"https://api.github.com/repos/dmfs/opentasks/git/commits{/sha}","comments_url":"https://api.github.com/repos/dmfs/opentasks/comments{/number}","issue_comment_url":"https://api.github.com/repos/dmfs/opentasks/issues/comments{/number}","contents_url":"https://api.github.com/repos/dmfs/opentasks/contents/{+path}","compare_url":"https://api.github.com/repos/dmfs/opentasks/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dmfs/opentasks/merges","archive_url":"https://api.github.com/repos/dmfs/opentasks/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dmfs/opentasks/downloads","issues_url":"https://api.github.com/repos/dmfs/opentasks/issues{/number}","pulls_url":"https://api.github.com/repos/dmfs/opentasks/pulls{/number}","milestones_url":"https://api.github.com/repos/dmfs/opentasks/milestones{/number}","notifications_url":"https://api.github.com/repos/dmfs/opentasks/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dmfs/opentasks/labels{/name}","releases_url":"https://api.github.com/repos/dmfs/opentasks/releases{/id}","deployments_url":"https://api.github.com/repos/dmfs/opentasks/deployments","created_at":"2013-06-07T13:18:53Z","updated_at":"2017-09-18T11:23:03Z","pushed_at":"2017-09-18T15:53:01Z","git_url":"git://github.com/dmfs/opentasks.git","ssh_url":"git@github.com:dmfs/opentasks.git","clone_url":"https://github.com/dmfs/opentasks.git","svn_url":"https://github.com/dmfs/opentasks","homepage":null,"size":23781,"stargazers_count":444,"watchers_count":444,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":139,"mirror_url":null,"open_issues_count":188,"forks":139,"open_issues":188,"watchers":444,"default_branch":"master"}},"base":{"label":"dmfs:issues/421-opentaskpal","ref":"issues/421-opentaskpal","sha":"d8783519dbba1d1e6c5fab856898733f467ac78f","user":{"login":"dmfs","id":1614987,"avatar_url":"https://avatars3.githubusercontent.com/u/1614987?v=4","gravatar_id":"","url":"https://api.github.com/users/dmfs","html_url":"https://github.com/dmfs","followers_url":"https://api.github.com/users/dmfs/followers","following_url":"https://api.github.com/users/dmfs/following{/other_user}","gists_url":"https://api.github.com/users/dmfs/gists{/gist_id}","starred_url":"https://api.github.com/users/dmfs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dmfs/subscriptions","organizations_url":"https://api.github.com/users/dmfs/orgs","repos_url":"https://api.github.com/users/dmfs/repos","events_url":"https://api.github.com/users/dmfs/events{/privacy}","received_events_url":"https://api.github.com/users/dmfs/received_events","type":"User","site_admin":false},"repo":{"id":10550221,"name":"opentasks","full_name":"dmfs/opentasks","owner":{"login":"dmfs","id":1614987,"avatar_url":"https://avatars3.githubusercontent.com/u/1614987?v=4","gravatar_id":"","url":"https://api.github.com/users/dmfs","html_url":"https://github.com/dmfs","followers_url":"https://api.github.com/users/dmfs/followers","following_url":"https://api.github.com/users/dmfs/following{/other_user}","gists_url":"https://api.github.com/users/dmfs/gists{/gist_id}","starred_url":"https://api.github.com/users/dmfs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dmfs/subscriptions","organizations_url":"https://api.github.com/users/dmfs/orgs","repos_url":"https://api.github.com/users/dmfs/repos","events_url":"https://api.github.com/users/dmfs/events{/privacy}","received_events_url":"https://api.github.com/users/dmfs/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dmfs/opentasks","description":"A task app for Android","fork":false,"url":"https://api.github.com/repos/dmfs/opentasks","forks_url":"https://api.github.com/repos/dmfs/opentasks/forks","keys_url":"https://api.github.com/repos/dmfs/opentasks/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dmfs/opentasks/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dmfs/opentasks/teams","hooks_url":"https://api.github.com/repos/dmfs/opentasks/hooks","issue_events_url":"https://api.github.com/repos/dmfs/opentasks/issues/events{/number}","events_url":"https://api.github.com/repos/dmfs/opentasks/events","assignees_url":"https://api.github.com/repos/dmfs/opentasks/assignees{/user}","branches_url":"https://api.github.com/repos/dmfs/opentasks/branches{/branch}","tags_url":"https://api.github.com/repos/dmfs/opentasks/tags","blobs_url":"https://api.github.com/repos/dmfs/opentasks/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dmfs/opentasks/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dmfs/opentasks/git/refs{/sha}","trees_url":"https://api.github.com/repos/dmfs/opentasks/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dmfs/opentasks/statuses/{sha}","languages_url":"https://api.github.com/repos/dmfs/opentasks/languages","stargazers_url":"https://api.github.com/repos/dmfs/opentasks/stargazers","contributors_url":"https://api.github.com/repos/dmfs/opentasks/contributors","subscribers_url":"https://api.github.com/repos/dmfs/opentasks/subscribers","subscription_url":"https://api.github.com/repos/dmfs/opentasks/subscription","commits_url":"https://api.github.com/repos/dmfs/opentasks/commits{/sha}","git_commits_url":"https://api.github.com/repos/dmfs/opentasks/git/commits{/sha}","comments_url":"https://api.github.com/repos/dmfs/opentasks/comments{/number}","issue_comment_url":"https://api.github.com/repos/dmfs/opentasks/issues/comments{/number}","contents_url":"https://api.github.com/repos/dmfs/opentasks/contents/{+path}","compare_url":"https://api.github.com/repos/dmfs/opentasks/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dmfs/opentasks/merges","archive_url":"https://api.github.com/repos/dmfs/opentasks/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dmfs/opentasks/downloads","issues_url":"https://api.github.com/repos/dmfs/opentasks/issues{/number}","pulls_url":"https://api.github.com/repos/dmfs/opentasks/pulls{/number}","milestones_url":"https://api.github.com/repos/dmfs/opentasks/milestones{/number}","notifications_url":"https://api.github.com/repos/dmfs/opentasks/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dmfs/opentasks/labels{/name}","releases_url":"https://api.github.com/repos/dmfs/opentasks/releases{/id}","deployments_url":"https://api.github.com/repos/dmfs/opentasks/deployments","created_at":"2013-06-07T13:18:53Z","updated_at":"2017-09-18T11:23:03Z","pushed_at":"2017-09-18T15:53:01Z","git_url":"git://github.com/dmfs/opentasks.git","ssh_url":"git@github.com:dmfs/opentasks.git","clone_url":"https://github.com/dmfs/opentasks.git","svn_url":"https://github.com/dmfs/opentasks","homepage":null,"size":23781,"stargazers_count":444,"watchers_count":444,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":139,"mirror_url":null,"open_issues_count":188,"forks":139,"open_issues":188,"watchers":444,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dmfs/opentasks/pulls/418"},"html":{"href":"https://github.com/dmfs/opentasks/pull/418"},"issue":{"href":"https://api.github.com/repos/dmfs/opentasks/issues/418"},"comments":{"href":"https://api.github.com/repos/dmfs/opentasks/issues/418/comments"},"review_comments":{"href":"https://api.github.com/repos/dmfs/opentasks/pulls/418/comments"},"review_comment":{"href":"https://api.github.com/repos/dmfs/opentasks/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dmfs/opentasks/pulls/418/commits"},"statuses":{"href":"https://api.github.com/repos/dmfs/opentasks/statuses/fbbbcab95ee0b63cc82eb22c04131210b1f87f3e"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"lemonboston"}}