diff --git a/.env.example b/.env.example index 0cb8c4f..43a6449 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -RECLAIM_ACCESS_TOKEN=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \ No newline at end of file +RECLAIM_ACCESS_TOKEN=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +RECLAIM_VERBOSE=false \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index afe1942..c1fbed1 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/example/index.ts b/example/index.ts index 707a425..53388b9 100644 --- a/example/index.ts +++ b/example/index.ts @@ -4,10 +4,10 @@ import { ReclaimClient } from "../src"; const client = new ReclaimClient(); const createTaskExample = async () => { - console.log("Create a task =>"); + console.log("\n\nCreate a task =>\n"); const newTask = await client.tasks.create({ - "title": "Test", + "title": "Funky Imitation Game", "eventColor": null, "eventCategory": "WORK", "timeChunksRequired": 4, @@ -21,74 +21,67 @@ const createTaskExample = async () => { "onDeck": false }); - console.log(newTask, "\n\n"); - return newTask; } const searchTasksExample = async () => { - console.log("Search tasks =>"); - - const tasks = await client.tasks.search({title: "Test"}); + console.log("\n\nSearch tasks =>\n"); - console.log(tasks, "\n\n"); + const tasks = await client.tasks.search({title: "Funky Imitation Game"}); return tasks; } const updateTaskExample = async (taskId: number) => { - console.log("Update a task =>"); + console.log("\n\nUpdate a task =>\n"); const updatedTask = await client.tasks.update(taskId, { - "title": "Test 2", + "title": "Indistinguishable Turing Test", }); - console.log(updatedTask, "\n\n"); - return updatedTask; } const getTaskExample = async (taskId: number) => { - console.log("Get a task =>"); + console.log("\n\nGet a task =>\n"); const task = await client.tasks.get(taskId); - console.log(task, "\n\n"); - return task; } const deleteTaskExample = async (taskId: number) => { - console.log("Delete a task =>"); + console.log("\n\nDelete a task =>\n"); const deletedTask = await client.tasks.delete(taskId); - console.log(deletedTask, "\n\n"); - return deletedTask; } const getCurrentUserExample = async () => { - console.log("Get current user =>"); + console.log("\n\nGet current user =>\n"); const user = await client.users.current(); - console.log(user, "\n\n"); - return user; } const updateCurrentUserExample = async () => { - console.log("Update current user =>"); + console.log("\n\nUpdate current user =>\n"); const updatedUser = await client.users.update({ companyName: "Assembless" }); - console.log(updatedUser, "\n\n"); - console.log(updatedUser.metadata.companyName) - return updatedUser; } +const markTaskAsDoneExample = async (taskId: number) => { + console.log("\n\nMark task as done =>\n"); + + const doneTask = await client.tasks.markDone(taskId); + + return doneTask; +} + // This is an example of how to use the ReclaimClient class. const main = async () => { console.clear(); @@ -102,6 +95,7 @@ const main = async () => { await searchTasksExample(); await updateTaskExample(taskId); await getTaskExample(taskId); + await markTaskAsDoneExample(taskId); await deleteTaskExample(taskId); } diff --git a/src/client.ts b/src/client.ts index d19ee22..c8b2c2b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -40,7 +40,7 @@ export class ReclaimClient { /** * @description A generic fetcher for the Reclaim API. * @param endpoint - * @param options + * @param options */ async _fetcher(endpoint: string, options: RequestInit) { const opts = { @@ -56,10 +56,25 @@ export class ReclaimClient { `${config.reclaim.apiUrl}/api/${endpoint}`, opts ); + + this.verbose( + `[${endpoint}] (${response.status})`, + `Response from Reclaim API`, + response + ); + return await response.json(); } catch (err) { console.error(err); throw new Error("Error fetching data from Reclaim API."); } } + + /** + * @description A verbose logger. + * @param args + */ + verbose(...args: any[]) { + if (config.verbose) console.log(...args); + } } diff --git a/src/config.ts b/src/config.ts index faafeae..3ec2f16 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,7 @@ const RECLAIM_API_URL = "https://api.app.reclaim.ai"; const RECLAIM_ACCESS_TOKEN = process?.env?.RECLAIM_ACCESS_TOKEN; +const RECLAIM_VERBOSE = process?.env?.RECLAIM_VERBOSE === "true"; /** * @description Configuration for the Reclaim API client. @@ -9,5 +10,6 @@ export const config = Object.freeze({ reclaim: { apiUrl: RECLAIM_API_URL, accessToken: RECLAIM_ACCESS_TOKEN - } + }, + verbose: RECLAIM_VERBOSE }); \ No newline at end of file diff --git a/src/modules/tasks.ts b/src/modules/tasks.ts index 88045c6..1141f73 100644 --- a/src/modules/tasks.ts +++ b/src/modules/tasks.ts @@ -79,4 +79,14 @@ export class ReclaimTasks { method: "DELETE", }); } + + /** + * @description Mark a task as done. + * @param id The ID of the task to mark as done. + */ + async markDone(id: number): Promise<{ taskOrHabit: ReclaimTask, events: unknown[] }> { + return await this.client._fetcher(`${ReclaimEndpoints.Planner}/done/task/${id}`, { + method: "POST", + }); + } } diff --git a/src/types.ts b/src/types.ts index cf33c15..0d3c6dd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,6 @@ export enum ReclaimEndpoints { Tasks = "tasks", + Planner = "planner", Users = "users", Calendars = "calendars", } diff --git a/src/utils.ts b/src/utils.ts index 9e176b5..8571c93 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -14,4 +14,4 @@ export function filterArray(array: T[], filters: Partial): T[] { return item[key] === filters[key]; }); }); -} +} \ No newline at end of file diff --git a/tests/mocks.ts b/tests/mocks.ts index 04d8efc..926842a 100644 --- a/tests/mocks.ts +++ b/tests/mocks.ts @@ -1,7 +1,7 @@ import { ReclaimTaskCreate } from "../src/types"; export const ReclaimTaskCreateMock: ReclaimTaskCreate = Object.freeze({ - title: "Test Task 1", + title: "Funky Imitation Game", eventColor: null, eventCategory: "WORK", timeChunksRequired: 8, @@ -14,3 +14,7 @@ export const ReclaimTaskCreateMock: ReclaimTaskCreate = Object.freeze({ due: "2029-11-21T16:30:00.000Z", onDeck: false, }); + +export const ReclaimTaskUpdateMock: Partial = Object.freeze({ + title: "Indistinguishable Turing Test", +}); \ No newline at end of file diff --git a/tests/modules/tasks.test.ts b/tests/modules/tasks.test.ts index 7877ab0..e874f13 100644 --- a/tests/modules/tasks.test.ts +++ b/tests/modules/tasks.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, test } from "bun:test"; import { ReclaimClient } from "../../src"; import { ReclaimTasks } from "../../src/modules/tasks"; -import { ReclaimTaskCreateMock } from "../mocks"; +import { ReclaimTaskCreateMock, ReclaimTaskUpdateMock } from "../mocks"; describe("ReclaimTasks", () => { let tasks: ReclaimTasks; @@ -37,15 +37,20 @@ describe("ReclaimTasks", () => { expect(filterResults[0].title).toBe(ReclaimTaskCreateMock.title); // Test task update - const updateResults = await tasks.update(createResults.id, { title: "Updated Test Task 1" }); + const updateResults = await tasks.update(createResults.id, ReclaimTaskUpdateMock); expect(updateResults).toBeTruthy(); expect(updateResults.id).toBe(updateResults.id); - expect(updateResults.title).toBe("Updated Test Task 1"); + expect(updateResults.title).toBe(ReclaimTaskUpdateMock.title); // Test task get const getResults = await tasks.get(createResults.id); expect(getResults).toBeTruthy(); expect(getResults.id).toBe(createResults.id); + + // Test task mark as done + const markAsDoneResults = await tasks.markDone(createResults.id); + expect(markAsDoneResults).toBeTruthy(); + expect(markAsDoneResults.taskOrHabit.id).toBe(createResults.id); // Test task delete const deleteResults = await tasks.delete(createResults.id);