From 78a1574c4f7084e5f001b6d69a946fb1d7359e99 Mon Sep 17 00:00:00 2001 From: Scott Lovegrove Date: Wed, 24 Aug 2022 12:55:19 +0100 Subject: [PATCH 01/11] chore: Updates existing entities to new types --- src/consts/endpoints.ts | 2 +- src/testUtils/testDefaults.ts | 47 +++++++++++---------- src/types/entities.ts | 72 ++++++++++++++++---------------- src/types/requests.ts | 16 ++++--- src/utils/taskConverters.test.ts | 18 ++++---- src/utils/taskConverters.ts | 16 ++++--- 6 files changed, 87 insertions(+), 84 deletions(-) diff --git a/src/consts/endpoints.ts b/src/consts/endpoints.ts index 3f15318..9b1e698 100644 --- a/src/consts/endpoints.ts +++ b/src/consts/endpoints.ts @@ -1,5 +1,5 @@ const BASE_URI = 'https://api.todoist.com' -const API_REST_BASE_URI = '/rest/v1/' +const API_REST_BASE_URI = '/rest/v2/' const API_SYNC_BASE_URI = '/sync/v8/' const TODOIST_URI = 'https://todoist.com' const API_AUTHORIZATION_BASE_URI = '/oauth/' diff --git a/src/testUtils/testDefaults.ts b/src/testUtils/testDefaults.ts index f346810..17d131d 100644 --- a/src/testUtils/testDefaults.ts +++ b/src/testUtils/testDefaults.ts @@ -9,26 +9,27 @@ import { Attachment, } from '../types' -const DEFAULT_TASK_ID = 1234 +const DEFAULT_TASK_ID = '1234' const DEFAULT_TASK_CONTENT = 'This is a task' const DEFAULT_TASK_DESCRIPTION = 'A description' const DEFAULT_TASK_PRIORITY = 1 const DEFAULT_ORDER = 3 -const DEFAULT_PROJECT_ID = 123 +const DEFAULT_PROJECT_ID = '123' const DEFAULT_PROJECT_NAME = 'This is a project' -const DEFAULT_LABEL_ID = 456 +const DEFAULT_PROJECT_VIEW_STYLE = 'list' +const DEFAULT_LABEL_ID = '456' const DEFAULT_LABEL_NAME = 'This is a label' -const DEFAULT_SECTION_ID = 456 +const DEFAULT_SECTION_ID = '456' const DEFAULT_SECTION_NAME = 'This is a section' -const DEFAULT_PARENT_ID = 5678 -const DEFAULT_ASSIGNEE = 1234 +const DEFAULT_PARENT_ID = '5678' +const DEFAULT_ASSIGNEE = '1234' const DEFAULT_DATE = '2020-09-08T12:00:00Z' -const DEFAULT_ENTITY_COLOR = 30 -const DEFAULT_LABELS = [1, 2, 3] -const DEFAULT_USER_ID = 5 +const DEFAULT_ENTITY_COLOR = 'berry_red' +const DEFAULT_LABELS = ['personal', 'work', 'hobby'] +const DEFAULT_USER_ID = '5' const DEFAULT_USER_NAME = 'A User' const DEFAULT_USER_EMAIL = 'atestuser@doist.com' -const DEFAULT_COMMENT_ID = 4 +const DEFAULT_COMMENT_ID = '4' const DEFAULT_COMMENT_CONTENT = 'A comment' export const DEFAULT_AUTH_TOKEN = 'AToken' @@ -37,14 +38,14 @@ export const DEFAULT_REQUEST_ID = 'ARequestID' export const INVALID_ENTITY_ID = 'invalid/entity/id' as unknown as number export const DEFAULT_DUE_DATE = { - recurring: false, + isRecurring: false, string: 'a date string', date: DEFAULT_DATE, } export const INVALID_DUE_DATE = { ...DEFAULT_DUE_DATE, - recurring: 'false', + isRecurring: 'false', } export const DEFAULT_QUICK_ADD_RESPONSE: QuickAddTaskResponse = { @@ -60,7 +61,6 @@ export const DEFAULT_QUICK_ADD_RESPONSE: QuickAddTaskResponse = { responsibleUid: DEFAULT_ASSIGNEE, checked: 0, dateAdded: DEFAULT_DATE, - syncId: null, due: { date: DEFAULT_DATE, timezone: null, @@ -78,14 +78,14 @@ export const DEFAULT_TASK: Task = { description: DEFAULT_TASK_DESCRIPTION, projectId: DEFAULT_PROJECT_ID, sectionId: DEFAULT_SECTION_ID, - completed: false, - labelIds: DEFAULT_LABELS, + isCompleted: false, + labels: DEFAULT_LABELS, priority: DEFAULT_TASK_PRIORITY, commentCount: 0, - created: DEFAULT_DATE, + createdAt: DEFAULT_DATE, url: 'https://todoist.com/showTask?id=1234', due: DEFAULT_DUE_DATE, - assignee: DEFAULT_ASSIGNEE, + assigneeId: DEFAULT_ASSIGNEE, } export const INVALID_TASK = { @@ -100,8 +100,11 @@ export const DEFAULT_PROJECT: Project = { order: DEFAULT_ORDER, parentId: DEFAULT_PROJECT_ID, commentCount: 0, - favorite: false, - shared: false, + isFavorite: false, + isShared: false, + isInboxProject: false, + isTeamInbox: false, + viewStyle: DEFAULT_PROJECT_VIEW_STYLE, url: `https://todoist.com/showProject?id=123`, } @@ -127,12 +130,12 @@ export const DEFAULT_LABEL: Label = { name: DEFAULT_LABEL_NAME, color: DEFAULT_ENTITY_COLOR, order: DEFAULT_ORDER, - favorite: false, + isFavorite: false, } export const INVALID_LABEL = { ...DEFAULT_LABEL, - favorite: 'true', + isFavorite: 'true', } export const DEFAULT_USER: User = { @@ -163,7 +166,7 @@ export const DEFAULT_COMMENT: Comment = { content: DEFAULT_COMMENT_CONTENT, projectId: DEFAULT_PROJECT_ID, attachment: DEFAULT_ATTACHMENT, - posted: DEFAULT_DATE, + postedAt: DEFAULT_DATE, } export const INVALID_COMMENT = { diff --git a/src/types/entities.ts b/src/types/entities.ts index e7eee5e..9027151 100644 --- a/src/types/entities.ts +++ b/src/types/entities.ts @@ -11,7 +11,7 @@ import { } from 'runtypes' export const Int = NumberRunType.withConstraint( - (n) => Number.isInteger(n) || `${n} is not a valid entity id. Should be an integer`, + (n) => Number.isInteger(n) || `${n} is not a valid entity id. Should be a string`, ) export type TodoistEntity = { @@ -27,7 +27,7 @@ export type EntityInHierarchy = OrderedEntity & { } export const DueDate = Record({ - recurring: Boolean, + isRecurring: Boolean, string: String, date: String, }).And( @@ -40,63 +40,64 @@ export const DueDate = Record({ export type DueDate = Static export const Task = Record({ - id: Int, + id: String, order: Int, content: String, description: String, - projectId: Int, - sectionId: Int, - completed: Boolean, - labelIds: Array(Int), + projectId: String, + isCompleted: Boolean, + labels: Array(String), priority: Int, commentCount: Int, - created: String, + createdAt: String, url: String, }).And( Partial({ - parentId: Int, due: DueDate, - assignee: Int, + assigneeId: String, + assignerId: String, + parentId: String, + sectionId: String, }), ) export type Task = Static export const Project = Record({ - id: Int, + id: String, name: String, - color: Int, + color: String, commentCount: Int, - shared: Boolean, - favorite: Boolean, + isShared: Boolean, + isFavorite: Boolean, url: String, + isInboxProject: Boolean, + isTeamInbox: Boolean, + order: Int, + viewStyle: String, }).And( Partial({ - parentId: Int, - order: Int, - inboxProject: Boolean, - teamInbox: Boolean, - syncId: Int, + parentId: String, }), ) export type Project = Static export const Section = Record({ - id: Int, + id: String, order: Int, name: String, - projectId: Int, + projectId: String, }) export type Section = Static export const Label = Record({ - id: Int, + id: String, order: Int, name: String, - color: Int, - favorite: Boolean, + color: String, + isFavorite: Boolean, }) export type Label = Static @@ -122,13 +123,13 @@ export const Attachment = Record({ export type Attachment = Static export const Comment = Record({ - id: Int, + id: String, content: String, - posted: String, + postedAt: String, }).And( Partial({ - taskId: Int, - projectId: Int, + taskId: String, + projectId: String, attachment: Attachment, }), ) @@ -136,7 +137,7 @@ export const Comment = Record({ export type Comment = Static export const User = Record({ - id: Int, + id: String, name: String, email: String, }) @@ -149,19 +150,18 @@ export type Color = TodoistEntity & { } export type QuickAddTaskResponse = { - id: number - projectId: number + id: string + projectId: string content: string description: string priority: number - sectionId: number | null - parentId: number | null + sectionId: string | null + parentId: string | null childOrder: number // order - labels: number[] // labelIds - responsibleUid: number | null + labels: string[] // labelIds + responsibleUid: string | null checked: number // completed dateAdded: string // created - syncId: number | null due: { date: string timezone: string | null diff --git a/src/types/requests.ts b/src/types/requests.ts index 5031a1b..8f0ed0d 100644 --- a/src/types/requests.ts +++ b/src/types/requests.ts @@ -11,7 +11,7 @@ export type AddTaskArgs = { dueLang?: string dueDate?: string dueDatetime?: string - assignee?: number + assigneeId?: number } export type QuickAddTaskArgs = { @@ -39,20 +39,24 @@ export type UpdateTaskArgs = { dueLang?: string dueDate?: string dueDatetime?: string - assignee?: number + assigneeId?: number } +export type ProjectViewStyle = 'list' | 'board' + export type AddProjectArgs = { name: string parentId?: number color?: number - favorite?: boolean + isFavorite?: boolean + viewStyle?: ProjectViewStyle } export type UpdateProjectArgs = { name?: string color?: number - favorite?: boolean + isFavorite?: boolean + viewStyle?: ProjectViewStyle } export type AddSectionArgs = { @@ -69,14 +73,14 @@ export type AddLabelArgs = { name: string order?: number color?: number - favorite?: boolean + isFavorite?: boolean } export type UpdateLabelArgs = { name?: string order?: number color?: number - favorite?: boolean + isFavorite?: boolean } export type GetTaskCommentsArgs = { diff --git a/src/utils/taskConverters.test.ts b/src/utils/taskConverters.test.ts index d1b9696..43ec8ca 100644 --- a/src/utils/taskConverters.test.ts +++ b/src/utils/taskConverters.test.ts @@ -7,13 +7,13 @@ describe('getTaskFromQuickAddResponse', () => { expect(task).toEqual(DEFAULT_TASK) }) - test('converts null sectionId to 0', () => { + test('converts null sectionId to null', () => { const quickAddResponse = { ...DEFAULT_QUICK_ADD_RESPONSE, sectionId: null, } const task = getTaskFromQuickAddResponse(quickAddResponse) - expect(task.sectionId).toEqual(0) + expect(task.sectionId).toEqual(undefined) }) test('converts null parentId to undefined', () => { @@ -25,13 +25,13 @@ describe('getTaskFromQuickAddResponse', () => { expect(task.parentId).toEqual(undefined) }) - test('converts null assignee to undefined', () => { + test('converts null assigneeId to undefined', () => { const quickAddResponse = { ...DEFAULT_QUICK_ADD_RESPONSE, responsibleUid: null, } const task = getTaskFromQuickAddResponse(quickAddResponse) - expect(task.assignee).toEqual(undefined) + expect(task.assigneeId).toEqual(undefined) }) const completedTheories = [ @@ -48,7 +48,7 @@ describe('getTaskFromQuickAddResponse', () => { } const task = getTaskFromQuickAddResponse(quickAddResponse) - expect(task.completed).toEqual(completedBoolean) + expect(task.isCompleted).toEqual(completedBoolean) }, ) @@ -62,16 +62,14 @@ describe('getTaskFromQuickAddResponse', () => { }) const taskUrlTheories = [ - [1234, null, 'https://todoist.com/showTask?id=1234'], - [1234, 0, 'https://todoist.com/showTask?id=1234'], - [1234, 5678, 'https://todoist.com/showTask?id=1234&sync_id=5678'], + ['1234', 'https://todoist.com/showTask?id=1234'], + ['1234', 'https://todoist.com/showTask?id=1234'], ] as const - test.each(taskUrlTheories)('with id %p and syncId %p returns url %p', (id, syncId, url) => { + test.each(taskUrlTheories)('with id %p and syncId %p returns url %p', (id, url) => { const quickAddResponse = { ...DEFAULT_QUICK_ADD_RESPONSE, id, - syncId, } const task = getTaskFromQuickAddResponse(quickAddResponse) expect(task.url).toEqual(url) diff --git a/src/utils/taskConverters.ts b/src/utils/taskConverters.ts index 6bd0080..cfd5e6a 100644 --- a/src/utils/taskConverters.ts +++ b/src/utils/taskConverters.ts @@ -3,15 +3,13 @@ import { QuickAddTaskResponse, Task } from '../types' const showTaskEndpoint = 'https://todoist.com/showTask' function getTaskUrlFromQuickAddResponse(responseData: QuickAddTaskResponse) { - return responseData.syncId - ? `${showTaskEndpoint}?id=${responseData.id}&sync_id=${responseData.syncId}` - : `${showTaskEndpoint}?id=${responseData.id}` + return `${showTaskEndpoint}?id=${responseData.id}` } export function getTaskFromQuickAddResponse(responseData: QuickAddTaskResponse): Task { const due = responseData.due ? { - recurring: responseData.due.isRecurring, + isRecurring: responseData.due.isRecurring, string: responseData.due.string, date: responseData.due.date, ...(responseData.due.timezone !== null && { datetime: responseData.due.date }), @@ -25,16 +23,16 @@ export function getTaskFromQuickAddResponse(responseData: QuickAddTaskResponse): content: responseData.content, description: responseData.description, projectId: responseData.projectId, - sectionId: responseData.sectionId ?? 0, - completed: responseData.checked === 1, - labelIds: responseData.labels, + sectionId: responseData.sectionId ?? undefined, + isCompleted: responseData.checked === 1, + labels: responseData.labels, priority: responseData.priority, commentCount: 0, // Will always be 0 for a quick add - created: responseData.dateAdded, + createdAt: responseData.dateAdded, url: getTaskUrlFromQuickAddResponse(responseData), ...(due !== undefined && { due }), ...(responseData.parentId !== null && { parentId: responseData.parentId }), - ...(responseData.responsibleUid !== null && { assignee: responseData.responsibleUid }), + ...(responseData.responsibleUid !== null && { assigneeId: responseData.responsibleUid }), } return task From f2a667b1dbac0788102f3d4c4116c10c81fb4a91 Mon Sep 17 00:00:00 2001 From: Scott Lovegrove Date: Wed, 24 Aug 2022 12:58:42 +0100 Subject: [PATCH 02/11] chore: Updates Color helper functions --- src/utils/colors.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/colors.ts b/src/utils/colors.ts index 403854c..3c6028a 100644 --- a/src/utils/colors.ts +++ b/src/utils/colors.ts @@ -44,7 +44,12 @@ export const colors = [ taupe, ] -export function getColor(colorId: number): Color { +export function getColorById(colorId: number): Color { const color = colors.find((color) => color.id === colorId) return color ?? charcoal } + +export function getColorByName(colorName: string): Color { + const color = colors.find((color) => color.name === colorName) + return color ?? charcoal +} From 100efccc6db4de9a37c0b43bdc898991635aabc1 Mon Sep 17 00:00:00 2001 From: Scott Lovegrove Date: Wed, 24 Aug 2022 13:21:42 +0100 Subject: [PATCH 03/11] chore: Updates request types --- src/types/requests.ts | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/types/requests.ts b/src/types/requests.ts index 8f0ed0d..ff42d53 100644 --- a/src/types/requests.ts +++ b/src/types/requests.ts @@ -1,17 +1,17 @@ export type AddTaskArgs = { content: string description?: string - projectId?: number - sectionId?: number - parentId?: number + projectId?: string + sectionId?: string + parentId?: string order?: number - labelIds?: number[] + labels?: string[] priority?: number dueString?: string dueLang?: string dueDate?: string dueDatetime?: string - assigneeId?: number + assigneeId?: string } export type QuickAddTaskArgs = { @@ -22,9 +22,9 @@ export type QuickAddTaskArgs = { } export type GetTasksArgs = { - projectId?: number - sectionId?: number - labelId?: number + projectId?: string + sectionId?: string + label?: string filter?: string lang?: string ids?: number[] @@ -33,20 +33,20 @@ export type GetTasksArgs = { export type UpdateTaskArgs = { content?: string description?: string - labelIds?: number[] + labels?: string[] priority?: number dueString?: string dueLang?: string dueDate?: string dueDatetime?: string - assigneeId?: number + assigneeId?: string } export type ProjectViewStyle = 'list' | 'board' export type AddProjectArgs = { name: string - parentId?: number + parentId?: string color?: number isFavorite?: boolean viewStyle?: ProjectViewStyle @@ -61,7 +61,7 @@ export type UpdateProjectArgs = { export type AddSectionArgs = { name: string - projectId: number + projectId: string order?: number } @@ -84,12 +84,12 @@ export type UpdateLabelArgs = { } export type GetTaskCommentsArgs = { - taskId: number + taskId: string projectId?: never } export type GetProjectCommentsArgs = { - projectId: number + projectId: string taskId?: never } @@ -104,15 +104,24 @@ type AddCommentArgs = { } export type AddTaskCommentArgs = AddCommentArgs & { - taskId: number + taskId: string projectId?: never } export type AddProjectCommentArgs = AddCommentArgs & { - projectId: number + projectId: string taskId?: never } export type UpdateCommentArgs = { content: string } + +export type RenameSharedLabelArgs = { + name: string + newName: string +} + +export type RemoveSharedLabelArgs = { + name: string +} From 449cf5a8a5635f5f028108dd4f937eba79e935f5 Mon Sep 17 00:00:00 2001 From: Scott Lovegrove Date: Wed, 24 Aug 2022 13:22:07 +0100 Subject: [PATCH 04/11] feat: Adds new shared labels endpoints and functions --- src/TodoistApi.ts | 51 +++++++++++++++++++++++++++++++++++++++++ src/consts/endpoints.ts | 3 +++ 2 files changed, 54 insertions(+) diff --git a/src/TodoistApi.ts b/src/TodoistApi.ts index 8b2d1a7..a49f0e1 100644 --- a/src/TodoistApi.ts +++ b/src/TodoistApi.ts @@ -24,6 +24,8 @@ import { UpdateSectionArgs, UpdateTaskArgs, QuickAddTaskArgs, + RenameSharedLabelArgs, + RemoveSharedLabelArgs, } from './types/requests' import { request, isSuccess } from './restClient' import { getTaskFromQuickAddResponse } from './utils/taskConverters' @@ -39,6 +41,9 @@ import { ENDPOINT_REST_PROJECT_COLLABORATORS, ENDPOINT_REST_SECTIONS, ENDPOINT_REST_COMMENTS, + ENDPOINT_REST_LABELS_SHARED, + ENDPOINT_REST_LABELS_SHARED_RENAME, + ENDPOINT_REST_LABELS_SHARED_REMOVE, } from './consts/endpoints' import { validateComment, @@ -319,6 +324,9 @@ export class TodoistApi { return isSuccess(response) } + /** + * Fetches a personal label + */ async getLabel(id: number): Promise