Skip to content

Commit

Permalink
✨ Implement habits module
Browse files Browse the repository at this point in the history
  • Loading branch information
DRFR0ST committed Dec 23, 2023
1 parent d50bf7c commit 0566c88
Show file tree
Hide file tree
Showing 10 changed files with 511 additions and 20 deletions.
71 changes: 54 additions & 17 deletions example/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
import { ReclaimClient } from "../src";
import { ReclaimHabitCreateMock, ReclaimHabitUpdateMock, ReclaimTaskCreateMock, ReclaimTaskUpdateMock } from "../tests/mocks";

// Create a new ReclaimClient instance.
const client = new ReclaimClient();

const createTaskExample = async () => {
console.log("\n\nCreate a task =>\n");

const newTask = await client.tasks.create({
"title": "Funky Imitation Game",
"eventColor": null,
"eventCategory": "WORK",
"timeChunksRequired": 4,
"minChunkSize": 4,
"maxChunkSize": 8,
"alwaysPrivate": true,
"timeSchemeId": "989b3027-46c4-4729-bdec-1070fc4d8c0f",
"priority": "P2",
"snoozeUntil": null,
"due": "2023-12-17T16:00:00.000Z",
"onDeck": false
});
const newTask = await client.tasks.create(ReclaimTaskCreateMock);

return newTask;
}
Expand All @@ -35,9 +23,7 @@ const searchTasksExample = async () => {
const updateTaskExample = async (taskId: number) => {
console.log("\n\nUpdate a task =>\n");

const updatedTask = await client.tasks.update(taskId, {
"title": "Indistinguishable Turing Test",
});
const updatedTask = await client.tasks.update(taskId, ReclaimTaskUpdateMock);

return updatedTask;
}
Expand Down Expand Up @@ -82,6 +68,46 @@ const markTaskAsDoneExample = async (taskId: number) => {
return doneTask;
}

const createHabitExample = async () => {
console.log("\n\nCreate a habit =>\n");

const newHabit = await client.habits.create(ReclaimHabitCreateMock);

return newHabit;
}

const updateHabitExample = async (habitId: number) => {
console.log("\n\nUpdate a habit =>\n");

const updatedHabit = await client.habits.update(habitId, ReclaimHabitUpdateMock);

return updatedHabit;
}

const searchHabitsExample = async () => {
console.log("\n\nSearch habits =>\n");

const habits = await client.habits.search({ title: ReclaimHabitCreateMock.title });

return habits;
}

const getHabitExample = async (habitId: number) => {
console.log("\n\nGet a habit =>\n");

const habit = await client.habits.get(habitId);

return habit;
}

const deleteHabitExample = async (habitId: number) => {
console.log("\n\nDelete a habit =>\n");

const deletedHabit = await client.habits.delete(habitId);

return deletedHabit;
}

// This is an example of how to use the ReclaimClient class.
const main = async () => {
console.clear();
Expand All @@ -97,6 +123,17 @@ const main = async () => {
await getTaskExample(taskId);
await markTaskAsDoneExample(taskId);
await deleteTaskExample(taskId);

const createdHabit = await createHabitExample();
const habitId = createdHabit.id;

await searchHabitsExample();

await updateHabitExample(habitId);

await getHabitExample(habitId);

await deleteHabitExample(habitId);
}

main();
5 changes: 5 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReclaimTasks } from "./modules/tasks";
import { ReclaimUsers } from "./modules/users";
import { ReclaimCalendars } from "./modules/calendars";
import { ReclaimHabits } from "./modules/habits";
import { config } from "./config";

/**
Expand Down Expand Up @@ -37,6 +38,10 @@ export class ReclaimClient {
return new ReclaimCalendars(this);
}

get habits() {
return new ReclaimHabits(this);
}

/**
* @description A generic fetcher for the Reclaim API.
* @param endpoint
Expand Down
80 changes: 80 additions & 0 deletions src/modules/habits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
ReclaimEndpoints,
ReclaimHabit,
ReclaimHabitCreate,
} from "../types";
import { ReclaimClient } from "../client";
import { filterArray } from "../utils";

/**
* A class for interacting with Reclaim habits.
*/
export class ReclaimHabits {
private path = ReclaimEndpoints.Habits;
private client: ReclaimClient;

constructor(client: ReclaimClient) {
this.client = client;
}

/**
* @description Create a new habit.
* @param habit The habit to create.
* @returns {ReclaimHabit}
*/
async create(habit: ReclaimHabitCreate): Promise<ReclaimHabit> {
const request = await this.client._fetcher(this.path, {
method: "POST",
body: JSON.stringify(habit),
});

const createdHabit = request.find((h: ReclaimHabit) => h.title === habit.title);

return createdHabit;
}

async search(filters?: Partial<ReclaimHabit>): Promise<ReclaimHabit[]> {
const response = (await this.client._fetcher(this.path, {
method: "GET",
})) as ReclaimHabit[];

if (!filters) return response;

// Filter the response based on the filter object
return filterArray(response, filters);
}

/**
* @description Get a single habit by ID.
* @param id The ID of the habit to get.
* @returns {ReclaimHabit | null}
*/
async get(id: number): Promise<ReclaimHabit | null> {
const habits = await this.search();

return habits.find((habit) => habit.id === id) || null;
}

/**
* @description Update a habit.
* @param habit The habit to update.
* @returns {ReclaimHabit}
*/
async update(id: number, habit: Partial<ReclaimHabitCreate>): Promise<ReclaimHabit> {
return await this.client._fetcher(`${this.path}/${id}`, {
method: "PATCH",
body: JSON.stringify(habit),
});
}

/**
* @description Delete a habit.
* @param id The ID of the habit to delete.
* @returns {null}
*/
async delete(id: number): Promise<{ taskOrHabit: ReclaimHabit, events: unknown[] }> {
return await this.client._fetcher(`planner/policy/habit/${id}`, {
method: "DELETE",
});
}
}
2 changes: 1 addition & 1 deletion src/modules/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class ReclaimTasks {
* @param task The task to update.
* @returns {ReclaimTask}
*/
async update(id: number, task: Partial<ReclaimTask>): Promise<ReclaimTask> {
async update(id: number, task: Partial<ReclaimTaskCreate>): Promise<ReclaimTask> {
return await this.client._fetcher(`${this.path}/${id}`, {
method: "PATCH",
body: JSON.stringify(task),
Expand Down
78 changes: 77 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum ReclaimEndpoints {
Tasks = "tasks",
Habits = "assist/habits/daily",
Planner = "planner",
Users = "users",
Calendars = "calendars",
Expand Down Expand Up @@ -63,6 +64,79 @@ export type ReclaimTaskCreate = Pick<
| "onDeck"
>;

/**
* An interface representing a Reclaim habit.
* Warning: This interface was reverse engineered from the Reclaim API and may be incomplete.
*/
export interface ReclaimHabit {
id: number;
title: string;
additionalDescription?: string | null;
alwaysPrivate: boolean;
eventCategory: string;
eventSubType: string;
eventColor?: string | "PERSONAL" | "WORK" | null;
created: Date;
updated: Date;
defenseAggression: string | "DEFAULT";
recurringAssignmentType: string;
invitees: Array<string>;
enabled: boolean;
durationMin: number;
durationMax: number;
idealDay?: string | null;
idealTime: string;
index: number;
elevated: boolean;
type: string;
reservedWords: Array<string>;
notification: boolean;
timePolicyType: string;
oneOffPolicy: {
dayHours: Record<WeekDays, {
intervals: Array<{ start: string; end: string; duration?: number }>;
startOfDay?: string;
endOfDay?: string;
}>;
startOfWeek?: string;
endOfWeek?: string;
};
autoDecline: boolean;
adjusted: boolean;
snoozeUntil?: string | null;
recurrence?: any | null;
priority: string | "P0" | "P1" | "P2" | "P3" | "P4";
timeSchemeId: string | null;
timesPerPeriod: number;
}

export type ReclaimHabitCreate = Pick<
ReclaimHabit,
| "additionalDescription"
| "alwaysPrivate"
| "autoDecline"
| "defenseAggression"
| "durationMax"
| "durationMin"
| "enabled"
| "eventCategory"
| "eventColor"
| "idealDay"
| "idealTime"
| "index"
| "invitees"
| "notification"
| "oneOffPolicy"
| "priority"
| "recurrence"
| "reservedWords"
| "snoozeUntil"
| "timePolicyType"
| "timeSchemeId"
| "timesPerPeriod"
| "title"
>;

/**
* An interface representing a Reclaim user.
* Warning: This interface was reverse engineered from the Reclaim API and may be incomplete.
Expand Down Expand Up @@ -518,4 +592,6 @@ export interface ReclaimUser {
*/
export interface ReclaimCalendar {
id: number;
}
}

export type WeekDays = "SUNDAY" | "MONDAY" | "TUESDAY" | "WEDNESDAY" | "THURSDAY" | "FRIDAY" | "SATURDAY";
Loading

0 comments on commit 0566c88

Please sign in to comment.