// Google Tasks API Service
import { google } from 'googleapis';
import type { OAuth2Client } from 'google-auth-library';
import type { tasks_v1 } from 'googleapis';
// ─────────────────────────────────────────────────────────────────────────────
// TYPES
// ─────────────────────────────────────────────────────────────────────────────
export type TaskStatus = 'needsAction' | 'completed';
export interface TaskLink {
type?: string;
description?: string;
link?: string;
}
export interface Task {
id?: string;
title?: string;
notes?: string;
status?: TaskStatus;
due?: string;
completed?: string;
parent?: string;
position?: string;
updated?: string;
selfLink?: string;
links?: TaskLink[];
hidden?: boolean;
deleted?: boolean;
webViewLink?: string;
}
export interface TaskList {
id?: string;
title?: string;
updated?: string;
selfLink?: string;
}
export interface CreateTaskParams {
taskListId?: string;
title: string;
notes?: string;
due?: string;
status?: TaskStatus;
parent?: string;
previous?: string;
}
export interface UpdateTaskParams {
taskListId?: string;
taskId: string;
title?: string;
notes?: string;
due?: string;
status?: TaskStatus;
}
export interface MoveTaskParams {
taskListId?: string;
taskId: string;
parent?: string;
previous?: string;
}
export interface ListTasksParams {
taskListId?: string;
maxResults?: number;
pageToken?: string;
showCompleted?: boolean;
showDeleted?: boolean;
showHidden?: boolean;
dueMin?: string;
dueMax?: string;
completedMin?: string;
completedMax?: string;
updatedMin?: string;
}
// ─────────────────────────────────────────────────────────────────────────────
// SERVICE CLASS
// ─────────────────────────────────────────────────────────────────────────────
export class TasksService {
private tasks: tasks_v1.Tasks;
constructor(authClient: OAuth2Client) {
this.tasks = google.tasks({ version: 'v1', auth: authClient });
}
// ─────────────────────────────────────────────────────────────────────────
// TASK LISTS
// ─────────────────────────────────────────────────────────────────────────
/**
* List all task lists
*/
async listTaskLists(
maxResults = 100,
pageToken?: string
): Promise<{ taskLists: TaskList[]; nextPageToken?: string }> {
const response = await this.tasks.tasklists.list({
maxResults,
pageToken,
});
return {
taskLists: (response.data.items || []).map(this.mapTaskList),
nextPageToken: response.data.nextPageToken || undefined,
};
}
/**
* Get a specific task list
*/
async getTaskList(taskListId: string): Promise<TaskList> {
const response = await this.tasks.tasklists.get({
tasklist: taskListId,
});
return this.mapTaskList(response.data);
}
/**
* Create a new task list
*/
async createTaskList(title: string): Promise<TaskList> {
const response = await this.tasks.tasklists.insert({
requestBody: { title },
});
return this.mapTaskList(response.data);
}
/**
* Update a task list
*/
async updateTaskList(taskListId: string, title: string): Promise<TaskList> {
const response = await this.tasks.tasklists.update({
tasklist: taskListId,
requestBody: { title },
});
return this.mapTaskList(response.data);
}
/**
* Delete a task list
*/
async deleteTaskList(taskListId: string): Promise<void> {
await this.tasks.tasklists.delete({
tasklist: taskListId,
});
}
// ─────────────────────────────────────────────────────────────────────────
// TASKS
// ─────────────────────────────────────────────────────────────────────────
/**
* List tasks in a task list
*/
async listTasks(params: ListTasksParams = {}): Promise<{ tasks: Task[]; nextPageToken?: string }> {
const taskListId = params.taskListId || '@default';
const response = await this.tasks.tasks.list({
tasklist: taskListId,
maxResults: params.maxResults || 100,
pageToken: params.pageToken,
showCompleted: params.showCompleted,
showDeleted: params.showDeleted,
showHidden: params.showHidden,
dueMin: params.dueMin,
dueMax: params.dueMax,
completedMin: params.completedMin,
completedMax: params.completedMax,
updatedMin: params.updatedMin,
});
return {
tasks: (response.data.items || []).map(this.mapTask),
nextPageToken: response.data.nextPageToken || undefined,
};
}
/**
* Get a specific task
*/
async getTask(taskListId: string | undefined, taskId: string): Promise<Task> {
const response = await this.tasks.tasks.get({
tasklist: taskListId || '@default',
task: taskId,
});
return this.mapTask(response.data);
}
/**
* Create a new task
*/
async createTask(params: CreateTaskParams): Promise<Task> {
const taskListId = params.taskListId || '@default';
const requestBody: tasks_v1.Schema$Task = {
title: params.title,
};
if (params.notes) requestBody.notes = params.notes;
if (params.due) requestBody.due = params.due;
if (params.status) requestBody.status = params.status;
const response = await this.tasks.tasks.insert({
tasklist: taskListId,
parent: params.parent,
previous: params.previous,
requestBody,
});
return this.mapTask(response.data);
}
/**
* Update a task
*/
async updateTask(params: UpdateTaskParams): Promise<Task> {
const taskListId = params.taskListId || '@default';
// First get the current task to preserve unchanged fields
const current = await this.tasks.tasks.get({
tasklist: taskListId,
task: params.taskId,
});
const requestBody: tasks_v1.Schema$Task = {
...current.data,
};
if (params.title !== undefined) requestBody.title = params.title;
if (params.notes !== undefined) requestBody.notes = params.notes;
if (params.due !== undefined) requestBody.due = params.due;
if (params.status !== undefined) requestBody.status = params.status;
const response = await this.tasks.tasks.update({
tasklist: taskListId,
task: params.taskId,
requestBody,
});
return this.mapTask(response.data);
}
/**
* Complete a task
*/
async completeTask(taskListId: string | undefined, taskId: string): Promise<Task> {
return this.updateTask({
taskListId,
taskId,
status: 'completed',
});
}
/**
* Move a task
*/
async moveTask(params: MoveTaskParams): Promise<Task> {
const response = await this.tasks.tasks.move({
tasklist: params.taskListId || '@default',
task: params.taskId,
parent: params.parent,
previous: params.previous,
});
return this.mapTask(response.data);
}
/**
* Delete a task
*/
async deleteTask(taskListId: string | undefined, taskId: string): Promise<void> {
await this.tasks.tasks.delete({
tasklist: taskListId || '@default',
task: taskId,
});
}
/**
* Clear completed tasks from a list
*/
async clearCompletedTasks(taskListId: string | undefined): Promise<void> {
await this.tasks.tasks.clear({
tasklist: taskListId || '@default',
});
}
// ─────────────────────────────────────────────────────────────────────────
// HELPERS
// ─────────────────────────────────────────────────────────────────────────
private mapTaskList(data: tasks_v1.Schema$TaskList): TaskList {
return {
id: data.id || undefined,
title: data.title || undefined,
updated: data.updated || undefined,
selfLink: data.selfLink || undefined,
};
}
private mapTask(data: tasks_v1.Schema$Task): Task {
return {
id: data.id || undefined,
title: data.title || undefined,
notes: data.notes || undefined,
status: data.status as TaskStatus || undefined,
due: data.due || undefined,
completed: data.completed || undefined,
parent: data.parent || undefined,
position: data.position || undefined,
updated: data.updated || undefined,
selfLink: data.selfLink || undefined,
links: data.links?.map(l => ({
type: l.type || undefined,
description: l.description || undefined,
link: l.link || undefined,
})),
hidden: data.hidden || undefined,
deleted: data.deleted || undefined,
webViewLink: data.webViewLink || undefined,
};
}
}