From 29e0d3b0b51a4434c3e8c70522128012f1c9aa2e Mon Sep 17 00:00:00 2001 From: Olivier Patry Date: Sat, 28 Sep 2024 09:25:01 +0200 Subject: [PATCH] Add :google:tasks module --- google/tasks/build.gradle.kts | 41 +++ .../net/opatry/google/tasks/TaskListsApi.kt | 179 ++++++++++ .../net/opatry/google/tasks/TasksApi.kt | 309 ++++++++++++++++++ .../net/opatry/google/tasks/TasksScopes.kt | 28 ++ .../google/tasks/model/AssignmentInfo.kt | 44 +++ .../opatry/google/tasks/model/ContextType.kt | 49 +++ .../model/ProtobufTimestampSerializer.kt | 46 +++ .../opatry/google/tasks/model/ResourceType.kt | 38 +++ .../opatry/google/tasks/model/SurfaceInfo.kt | 59 ++++ .../net/opatry/google/tasks/model/Task.kt | 118 +++++++ .../net/opatry/google/tasks/model/TaskList.kt | 55 ++++ .../tasks/model/TaskListsListResponse.kt | 46 +++ .../google/tasks/model/TasksListResponse.kt | 46 +++ gradle/libs.versions.toml | 3 + settings.gradle.kts | 3 +- 15 files changed, 1063 insertions(+), 1 deletion(-) create mode 100644 google/tasks/build.gradle.kts create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TaskListsApi.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksApi.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksScopes.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/AssignmentInfo.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ContextType.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ProtobufTimestampSerializer.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ResourceType.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/SurfaceInfo.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/Task.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskList.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskListsListResponse.kt create mode 100644 google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TasksListResponse.kt diff --git a/google/tasks/build.gradle.kts b/google/tasks/build.gradle.kts new file mode 100644 index 00000000..f60f72da --- /dev/null +++ b/google/tasks/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +plugins { + alias(libs.plugins.jetbrains.kotlin.multiplatform) + alias(libs.plugins.jetbrains.kotlin.serialization) +} + +kotlin { + jvm() + + sourceSets { + commonMain.dependencies { + implementation(libs.kotlinx.datetime) + implementation(libs.bundles.ktor.client) + } + + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } +} diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TaskListsApi.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TaskListsApi.kt new file mode 100644 index 00000000..695d03cd --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TaskListsApi.kt @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks + +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.plugins.ClientRequestException +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.client.request.patch +import io.ktor.client.request.post +import io.ktor.client.request.put +import io.ktor.client.request.setBody +import io.ktor.client.statement.bodyAsText +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.http.isSuccess +import net.opatry.google.tasks.model.TaskList +import net.opatry.google.tasks.model.TaskListsListResponse + +/** + * Service for interacting with the [Google Task Lists REST API](https://developers.google.com/tasks/reference/rest/v1/tasklists). + */ +class TaskListsApi( + private val httpClient: HttpClient +) { + /** + * [Deletes the authenticated user's specified task list](https://developers.google.com/tasks/reference/rest/v1/tasklists/delete). If the list contains assigned tasks, both the assigned tasks and the original tasks in the assignment surface (Docs, Chat Spaces) are deleted. + * + * @param taskListId Task list identifier. + */ + suspend fun delete(taskListId: String) { + val response = httpClient.delete("tasks/v1/users/@me/lists/${taskListId}") + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Returns the authenticated user's specified task list](https://developers.google.com/tasks/reference/rest/v1/tasklists/get). + * + * @param taskListId Task list identifier. + * + * @return an instance of [TaskList]. + */ + suspend fun get(taskListId: String): TaskList { + val response = httpClient.get("tasks/v1/users/@me/lists/${taskListId}") + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Creates a new task list](https://developers.google.com/tasks/reference/rest/v1/tasklists/insert) and adds it to the authenticated user's task lists. A user can have up to 2000 lists at a time. + * + * @param taskList the task list to insert. + * + * @return a newly created instance of [TaskList]. + */ + suspend fun insert(taskList: TaskList): TaskList { + val response = httpClient.post("tasks/v1/users/@me/lists") { + contentType(ContentType.Application.Json) + setBody(taskList) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Returns all the authenticated user's task lists](https://developers.google.com/tasks/reference/rest/v1/tasklists/list). A user can have up to 2000 lists at a time. + * + * @param maxResults Maximum number of task lists returned on one page. Optional. The default is 20 (max allowed: 100). + * @param pageToken Token specifying the result page to return. Optional. + * + * @return an instance of [TaskListsListResponse]. + */ + suspend fun list(maxResults: Int = 20, pageToken: String? = null): TaskListsListResponse { + val response = httpClient.get("tasks/v1/users/@me/lists") { + parameter("maxResults", maxResults.coerceIn(0, 100)) + if (pageToken != null) { + parameter("pageToken", pageToken) + } + } + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * Iterate on [list]'s paginated results and returns single list of all task lists. + * + * @see [list] + */ + suspend fun listAll(): List { + var nextPageToken: String? = null + return buildList { + do { + val response = list(maxResults = 100, nextPageToken) + addAll(response.items) + nextPageToken = response.nextPageToken + } while (nextPageToken != null) + } + } + + /** + * [Updates the authenticated user's specified task list](https://developers.google.com/tasks/reference/rest/v1/tasklists/patch). This method supports patch semantics. + * + * @param taskListId Task list identifier. + * @param taskList the task list to patch. + * + * @return an instance of [TaskList]. + */ + suspend fun patch(taskListId: String, taskList: TaskList): TaskList { + val response = httpClient.patch("tasks/v1/users/@me/lists/${taskListId}") { + contentType(ContentType.Application.Json) + setBody(taskList) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Updates the authenticated user's specified task list](https://developers.google.com/tasks/reference/rest/v1/tasklists/update). + * + * @param taskListId Task list identifier. + * @param taskList the task list to update. + * + * @return an instance of [TaskList]. + */ + suspend fun update(taskListId: String, taskList: TaskList): TaskList { + val response = httpClient.put("tasks/v1/users/@me/lists/${taskListId}") { + contentType(ContentType.Application.Json) + setBody(taskList) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } +} \ No newline at end of file diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksApi.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksApi.kt new file mode 100644 index 00000000..6371c66a --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksApi.kt @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks + +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.plugins.ClientRequestException +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.client.request.patch +import io.ktor.client.request.post +import io.ktor.client.request.put +import io.ktor.client.request.setBody +import io.ktor.client.statement.bodyAsText +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.http.isSuccess +import kotlinx.datetime.Instant +import net.opatry.google.tasks.model.Task +import net.opatry.google.tasks.model.TasksListResponse + +/** + * Service for interacting with the [Google Tasks REST API](https://developers.google.com/tasks/reference/rest/v1/tasks). + */ +class TasksApi( + private val httpClient: HttpClient +) { + /** + * [Clears all completed tasks](https://developers.google.com/tasks/reference/rest/v1/tasks/clear) from the specified task list. The affected tasks will be marked as 'hidden' and no longer be returned by default when retrieving all tasks for a task list. + * + * @param taskListId Task list identifier. + */ + suspend fun clear(taskListId: String) { + val response = httpClient.post("tasks/v1/lists/${taskListId}/clear") + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Deletes the specified task](https://developers.google.com/tasks/reference/rest/v1/tasks/delete) from the task list. If the task is assigned, both the assigned task and the original task (in Docs, Chat Spaces) are deleted. To delete the assigned task only, navigate to the assignment surface and unassign the task from there. + * + * @param taskListId Task list identifier. + * @param taskId Task identifier. + */ + suspend fun delete(taskListId: String, taskId: String) { + val response = httpClient.delete("tasks/v1/lists/${taskListId}/tasks/${taskId}") + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Returns the specified task](https://developers.google.com/tasks/reference/rest/v1/tasks/get). + * + * @param taskListId Task list identifier. + * @param taskId Task identifier. + * + * @return an instance of [Task]. + */ + suspend fun get(taskListId: String, taskId: String): Task { + val response = httpClient.get("tasks/v1/lists/${taskListId}/tasks/${taskId}") { + contentType(ContentType.Application.Json) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Creates a new task](https://developers.google.com/tasks/reference/rest/v1/tasks/insert) on the specified task list. Tasks assigned from Docs or Chat Spaces cannot be inserted from Tasks Public API; they can only be created by assigning them from Docs or Chat Spaces. A user can have up to 20,000 non-hidden tasks per list and up to 100,000 tasks in total at a time. + * + * @param taskListId Task list identifier. + * @param task the task data to insert. + * @param parentTaskId Parent task identifier. If the task is created at the top level, this parameter is omitted. An assigned task cannot be a parent task, nor can it have a parent. Setting the parent to an assigned task results in failure of the request. + * @param previousTaskId Previous sibling task identifier. If the task is created at the first position among its siblings, this parameter is omitted. + * + * @return a newly created instance of [Task]. + */ + suspend fun insert(taskListId: String, task: Task, parentTaskId: String? = null, previousTaskId: String? = null): Task { + val response = httpClient.post("tasks/v1/lists/${taskListId}/tasks") { + if (parentTaskId != null) { + parameter("parent", parentTaskId) + } + if (previousTaskId != null) { + parameter("previous", previousTaskId) + } + contentType(ContentType.Application.Json) + setBody(task) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * Returns [all tasks in the specified task list](https://developers.google.com/tasks/reference/rest/v1/tasks/list). Does not return assigned tasks be default (from Docs, Chat Spaces). A user can have up to 20,000 non-hidden tasks per list and up to 100,000 tasks in total at a time. + * + * @param taskListId Task list identifier. + * @param completedMin Lower bound for a task's completion date (as an RFC 3339 timestamp) to filter by. The default is not to filter by completion date. + * @param completedMax Upper bound for a task's completion date (as an RFC 3339 timestamp) to filter by. The default is not to filter by completion date. + * @param dueMin Lower bound for a task's due date (as an RFC 3339 timestamp) to filter by. The default is not to filter by due date. + * @param dueMax Upper bound for a task's due date (as an RFC 3339 timestamp) to filter by. The default is not to filter by due date. + * @param maxResults Maximum number of tasks returned on one page. The default is 20 (max allowed: 100). + * @param pageToken Token specifying the result page to return. + * @param showCompleted Flag indicating whether completed tasks are returned in the result. Note that [showHidden] must also be `true` to show tasks completed in first party clients, such as the web UI and Google's mobile apps. The default is `true`. + * @param showDeleted Flag indicating whether deleted tasks are returned in the result. The default is `false`. + * @param showHidden Flag indicating whether hidden tasks are returned in the result. The default is `false`. + * @param updatedMin Lower bound for a task's last modification time (as an RFC 3339 timestamp) to filter by. The default is not to filter by last modification time. + * @param showAssigned Flag indicating whether tasks assigned to the current user are returned in the result. The default is `false`. + * + * @return an instance of [TasksListResponse]. + */ + suspend fun list( + taskListId: String, + completedMin: Instant? = null, + completedMax: Instant? = null, + dueMin: Instant? = null, + dueMax: Instant? = null, + maxResults: Int = 20, + pageToken: String? = null, + showCompleted: Boolean = true, + showDeleted: Boolean = false, + showHidden: Boolean = false, + updatedMin: Instant? = null, + showAssigned: Boolean = false + ): TasksListResponse { + val response = httpClient.get("tasks/v1/lists/${taskListId}/tasks") { + if (completedMin != null) { + parameter("completedMin", completedMin.toString()) + } + if (completedMax != null) { + parameter("completedMax", completedMax.toString()) + } + if (dueMin != null) { + parameter("dueMin", dueMin.toString()) + } + if (dueMax != null) { + parameter("dueMax", dueMax.toString()) + } + parameter("maxResults", maxResults.toString()) + if (pageToken != null) { + parameter("pageToken", pageToken) + } + parameter("showCompleted", showCompleted.toString()) + parameter("showDeleted", showDeleted.toString()) + parameter("showHidden", showHidden.toString()) + if (updatedMin != null) { + parameter("updatedMin", updatedMin.toString()) + } + parameter("showAssigned", showAssigned.toString()) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * Iterate on [list]'s paginated results and returns single list of all tasks. + * + * @see [list] + */ + suspend fun listAll( + taskListId: String, + completedMin: Instant? = null, + completedMax: Instant? = null, + dueMin: Instant? = null, + dueMax: Instant? = null, + showCompleted: Boolean = true, + showDeleted: Boolean = false, + showHidden: Boolean = false, + updatedMin: Instant? = null, + showAssigned: Boolean = false + ): List { + var nextPageToken: String? = null + return buildList { + do { + val response = list( + taskListId, + completedMin, + completedMax, + dueMin, + dueMax, + maxResults = 100, + nextPageToken, + showCompleted, + showDeleted, + showHidden, + updatedMin, + showAssigned + ) + addAll(response.items) + nextPageToken = response.nextPageToken + } while (nextPageToken != null) + } + } + + /** + * [Moves the specified task](https://developers.google.com/tasks/reference/rest/v1/tasks/move) to another position in the destination task list. + * + * @param taskListId Task list identifier. + * @param taskId Task identifier. + * @param parentTaskId New parent task identifier. If the task is moved to the top level, this parameter is omitted. Assigned tasks can not be set as parent task (have subtasks) or be moved under a parent task (become subtasks). + * @param previousTaskId New previous sibling task identifier. If the task is moved to the first position among its siblings, this parameter is omitted. + * @param destinationTaskListId Destination task list identifier. If set, the task is moved from [taskListId] to the [destinationTaskListId] list. Otherwise, the task is moved within its current list. Recurrent tasks cannot currently be moved between lists. + * + * @return an instance of [Task]. + */ + suspend fun move(taskListId: String, taskId: String, parentTaskId: String? = null, previousTaskId: String? = null, destinationTaskListId: String? = null): Task { + val response = httpClient.post("tasks/v1/lists/${taskListId}/tasks/${taskId}/move") { + if (parentTaskId != null) { + parameter("parent", parentTaskId) + } + if (previousTaskId != null) { + parameter("previous", previousTaskId) + } + if (destinationTaskListId != null) { + @Suppress("SpellCheckingInspection") + parameter("destinationTasklist", destinationTaskListId) + } + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Updates the specified task](https://developers.google.com/tasks/reference/rest/v1/tasks/patch). This method supports patch semantics. + * + * @param taskListId Task list identifier. + * @param taskId Task identifier. + * @param task the task data to patch. + * + * @return an instance of [Task]. + */ + suspend fun patch(taskListId: String, taskId: String, task: Task): Task { + val response = httpClient.patch("tasks/v1/lists/${taskListId}/tasks/${taskId}") { + contentType(ContentType.Application.Json) + setBody(task) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } + + /** + * [Updates the specified task](https://developers.google.com/tasks/reference/rest/v1/tasks/update). + * + * @param taskListId Task list identifier. + * @param taskId Task identifier. + * @param task the task data to update. + * + * @return an instance of [Task]. + */ + suspend fun update(taskListId: String, taskId: String, task: Task): Task { + val response = httpClient.put("tasks/v1/lists/${taskListId}/tasks/${taskId}") { + contentType(ContentType.Application.Json) + setBody(task) + } + + if (response.status.isSuccess()) { + return response.body() + } else { + throw ClientRequestException(response, response.bodyAsText()) + } + } +} \ No newline at end of file diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksScopes.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksScopes.kt new file mode 100644 index 00000000..3f66400b --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/TasksScopes.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks + +object TasksScopes { + const val Tasks = "https://www.googleapis.com/auth/tasks" + const val TasksReadOnly = "https://www.googleapis.com/auth/tasks.readonly" +} diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/AssignmentInfo.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/AssignmentInfo.kt new file mode 100644 index 00000000..af7c7a9d --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/AssignmentInfo.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +/** + * Information about the source of the [task assignment](https://developers.google.com/tasks/reference/rest/v1/tasks#assignmentinfo) (Document, Chat Space). + */ +data class AssignmentInfo( + @SerialName("linkToTask") + val linkToTask: String, + @SerialName("surfaceType") + val surfaceType: ContextType, + + // TODO Union field + @SerialName("driveResourceInfo") + val driveResourceInfo: SurfaceInfo.DriveResourceInfo? = null, + @SerialName("spaceInfo") + val spaceInfo: SurfaceInfo.SpaceInfo? = null, +) + diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ContextType.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ContextType.kt new file mode 100644 index 00000000..c3526dad --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ContextType.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import net.opatry.google.tasks.model.ContextType.GMail +import net.opatry.google.tasks.model.ContextType.Space +import net.opatry.google.tasks.model.ContextType.Unspecified + +@Serializable +/** + * The [product](https://developers.google.com/tasks/reference/rest/v1/tasks#contexttype) associated with the task. + * + * @property Unspecified Unknown value for this task's context. + * @property GMail The task is created from Gmail. + * @property javax.print.Doc The task is assigned from a document. + * @property Space The task is assigned from a Chat Space. + */ +enum class ContextType { + @SerialName("CONTEXT_TYPE_UNSPECIFIED") + Unspecified, + @SerialName("GMAIL") + GMail, + @SerialName("DOCUMENT") + Document, + @SerialName("SPACE") + Space, +} \ No newline at end of file diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ProtobufTimestampSerializer.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ProtobufTimestampSerializer.kt new file mode 100644 index 00000000..fdbbad7e --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ProtobufTimestampSerializer.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.datetime.Instant +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +object ProtobufTimestampSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("google.protobuf.Timestamp", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Instant) { + // Serialize the Instant as an ISO 8601 string (RFC 3339) + encoder.encodeString(value.toString()) + } + + override fun deserialize(decoder: Decoder): Instant { + // Deserialize the ISO 8601 string back to Instant + return Instant.parse(decoder.decodeString()) + } +} \ No newline at end of file diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ResourceType.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ResourceType.kt new file mode 100644 index 00000000..8b5d59d4 --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/ResourceType.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +enum class ResourceType { + @SerialName("tasks#taskList") + TaskList, + @SerialName("tasks#taskLists") + TaskLists, + @SerialName("tasks#task") + Task, + @SerialName("tasks#tasks") + Tasks, +} \ No newline at end of file diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/SurfaceInfo.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/SurfaceInfo.kt new file mode 100644 index 00000000..b8b4c36a --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/SurfaceInfo.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +/** + * Information about the surface (Docs, Chat Spaces) where this task was assigned from. + */ +abstract class SurfaceInfo { +// @SerialName("driveResourceInfo") + @Serializable + /** + * Information about the [Drive resource](https://developers.google.com/tasks/reference/rest/v1/tasks#driveresourceinfo) where a task was assigned from (the document, sheet, etc.). + * + * @property driveFileId Output only. Identifier of the file in the Drive API. + * @property resourceKey Output only. Resource key required to access files shared via a shared link. Not required for all files. See also developers.google.com/drive/api/guides/resource-keys. + */ + data class DriveResourceInfo( + @SerialName("driveFileId") + val driveFileId: String, + @SerialName("resourceKey") + val resourceKey: String, + ) : SurfaceInfo() + +// @SerialName("spaceInfo") + @Serializable + /** + * Information about the [Chat Space](https://developers.google.com/tasks/reference/rest/v1/tasks#spaceinfo) where a task was assigned from. + * + * @property space Output only. The Chat space where this task originates from. The format is "spaces/{space}". + */ + data class SpaceInfo( + @SerialName("space") + val space: String, + ) : SurfaceInfo() +} diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/Task.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/Task.kt new file mode 100644 index 00000000..0dfc8812 --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/Task.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import net.opatry.google.tasks.TasksApi +import net.opatry.google.tasks.model.Task.Status + +@Serializable +/** + * https://developers.google.com/tasks/reference/rest/v1/tasks#resource:-task + * + * @property kind Output only. Type of the resource. This is always [ResourceType.Task]. + * @property id Task identifier. + * @property etag ETag of the resource. + * @property title Title of the task. Maximum length allowed: 1024 characters. + * @property updatedDate Output only. Last modification time of the task (as a RFC 3339 timestamp). + * @property selfLink Output only. URL pointing to this task. Used to retrieve, update, or delete this task. + * @property parent Output only. Parent task identifier. This field is omitted if it is a top-level task. Use the [TasksApi.move] method to move the task under a different parent or to the top level. A parent task can never be an assigned task (from Chat Spaces, Docs). This field is read-only. + * @property position Output only. String indicating the position of the task among its sibling tasks under the same parent task or at the top level. If this string is greater than another task's corresponding position string according to lexicographical ordering, the task is positioned after the other task under the same parent task (or at the top level). Use the [TasksApi.move] method to move the task to another position. + * @property notes Notes describing the task. Tasks assigned from Google Docs cannot have notes. Optional. Maximum length allowed: 8192 characters. + * @property status Status of the task. This is either [Status.NeedsAction] or [Status.Completed]. + * @property dueDate Due date of the task (as a RFC 3339 timestamp). Optional. The due date only records date information; the time portion of the timestamp is discarded when setting the due date. It isn't possible to read or write the time that a task is due via the API. + * @property completedDate Completion date of the task (as a RFC 3339 timestamp). This field is omitted if the task has not been completed. + * @property isDeleted Flag indicating whether the task has been deleted. For assigned tasks this field is read-only. They can only be deleted by calling [TasksApi.delete], in which case both the assigned task and the original task (in Docs or Chat Spaces) are deleted. To delete the assigned task only, navigate to the assignment surface and unassign the task from there. The default is `false`. + * @property isHidden Flag indicating whether the task is hidden. This is the case if the task had been marked completed when the task list was last cleared. The default is `false`. This field is read-only. + * @property links Output only. Collection of links. This collection is read-only. + * @property webViewLink Output only. An absolute link to the task in the Google Tasks Web UI. + * @property assignmentInfo Output only. Context information for assigned tasks. A task can be assigned to a user, currently possible from surfaces like Docs and Chat Spaces. This field is populated for tasks assigned to the current user and identifies where the task was assigned from. This field is read-only. + */ +data class Task( + @SerialName("kind") + val kind: ResourceType = ResourceType.Task, + @SerialName("id") + val id: String = "", + @SerialName("etag") + val etag: String = "", + @SerialName("title") + val title: String, + @Serializable(with = ProtobufTimestampSerializer::class) + @SerialName("updated") + val updatedDate: Instant = Clock.System.now(), + @SerialName("selfLink") + val selfLink: String = "", + @SerialName("parent") + val parent: String? = null, + @SerialName("position") + val position: String = "", + @SerialName("notes") + val notes: String? = null, + @SerialName("status") + val status: Status = Status.NeedsAction, + @SerialName("due") + @Serializable(with = ProtobufTimestampSerializer::class) + val dueDate: Instant? = null, + @Serializable(with = ProtobufTimestampSerializer::class) + @SerialName("completed") + val completedDate: Instant? = null, + @SerialName("deleted") + val isDeleted: Boolean = false, + @SerialName("hidden") + val isHidden: Boolean = false, + @SerialName("links") + val links: List = emptyList(), + @SerialName("webViewLink") + val webViewLink: String = "", + @SerialName("assignmentInfo") + val assignmentInfo: AssignmentInfo? = null, +) { + val isCompleted = completedDate != null && status == Status.Completed + + @Serializable + enum class Status { + @SerialName("needsAction") + NeedsAction, + + @SerialName("completed") + Completed, + } + + @Serializable + /** + * @property type Type of the link, e.g. "email". + * @property description The description. In HTML speak: Everything between and . + * @property link The URL. + */ + data class Link( + @SerialName("type") + val type: String, + @SerialName("description") + val description: String, + @SerialName("link") + val link: String + ) +} diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskList.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskList.kt new file mode 100644 index 00000000..b71b8e3d --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskList.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +/** + * https://developers.google.com/tasks/reference/rest/v1/tasklists#resource:-tasklist + * + * @property kind Output only. Type of the resource. This is always [ResourceType.TaskList]. + * @property id Task list identifier. + * @property etag ETag of the resource. + * @property title Title of the task list. Maximum length allowed: 1024 characters. + * @property updatedDate Output only. Last modification time of the task list (as a RFC 3339 timestamp). + * @property selfLink Output only. URL pointing to this task list. Used to retrieve, update, or delete this task list. + */ +data class TaskList( + @SerialName("kind") + val kind: ResourceType = ResourceType.TaskList, + @SerialName("id") + val id: String = "", + @SerialName("etag") + val etag: String = "", + @SerialName("title") + val title: String, + @Serializable(with = ProtobufTimestampSerializer::class) + @SerialName("updated") + val updatedDate: Instant = Clock.System.now(), + @SerialName("selfLink") + val selfLink: String = "", +) diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskListsListResponse.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskListsListResponse.kt new file mode 100644 index 00000000..f0d299d4 --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TaskListsListResponse.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +/** + * https://developers.google.com/tasks/reference/rest/v1/tasklists/list#response-body + * + * @property kind Type of the resource. This is always [ResourceType.TaskLists]. + * @property etag ETag of the resource. + * @property nextPageToken Token that can be used to request the next page of this result. + * @property items Collection of task lists. + */ +data class TaskListsListResponse( + @SerialName("kind") + val kind: ResourceType = ResourceType.TaskLists, + @SerialName("etag") + val etag: String, + @SerialName("nextPageToken") + val nextPageToken: String? = null, + @SerialName("items") + val items: List, +) diff --git a/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TasksListResponse.kt b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TasksListResponse.kt new file mode 100644 index 00000000..5914e5b9 --- /dev/null +++ b/google/tasks/src/commonMain/kotlin/net/opatry/google/tasks/model/TasksListResponse.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.opatry.google.tasks.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +/** + * https://developers.google.com/tasks/reference/rest/v1/tasks/list#response-body + * + * @property kind Type of the resource. This is always [ResourceType.Tasks]. + * @property etag ETag of the resource. + * @property nextPageToken Token that can be used to request the next page of this result. + * @property items Collection of tasks. + */ +data class TasksListResponse( + @SerialName("kind") + val kind: ResourceType = ResourceType.Tasks, + @SerialName("etag") + val etag: String, + @SerialName("nextPageToken") + val nextPageToken: String? = null, + @SerialName("items") + val items: List, +) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index da467959..980494ea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,8 @@ ktor = "2.3.12" [libraries] kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.1" } + ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" } ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" } @@ -16,6 +18,7 @@ ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negoti ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } [bundles] ktor-server = ["ktor-server-core", "ktor-server-cio"] diff --git a/settings.gradle.kts b/settings.gradle.kts index 0e840a87..42877e21 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,4 +36,5 @@ dependencyResolutionManagement { rootProject.name = "google-tasks-kmp" -include(":google:oauth") \ No newline at end of file +include(":google:oauth") +include(":google:tasks") \ No newline at end of file