diff --git a/src/models/folders.ts b/src/models/folders.ts new file mode 100644 index 00000000..41e6ab10 --- /dev/null +++ b/src/models/folders.ts @@ -0,0 +1,86 @@ +/** + * Interface of a folder object from Nylas. + */ +export interface Folder { + /** + * A globally unique object identifier. + */ + id: string; + + /** + * Folder name. + */ + name: string; + + /** + * The type of object. + */ + object: string; + + /** + * Grant ID of the Nylas account + */ + grantId: string; + + /** + * (Google only) Folder background color. + */ + backgroundColor?: string; + + /** + * (Google only) Indicates if the folder is user created or system created. + */ + systemFolder?: boolean; + + /** + * (Google only) Folder text color. + */ + textColor?: string; + + /** + * (Microsoft only) The number of immediate child folders in the current folder. + */ + childCount?: number; + + /** + * (Microsoft only) ID of the parent folder. + */ + parentId?: string; + + /** + * The number of items inside of a folder. + */ + totalCount?: number; + + /** + * The number of unread items inside of a folder. + */ + unreadCount?: number; +} + +/** + * Interface for creating a new folder. + */ +export interface CreateFolderRequest { + /** + * Creates a folder with the specified display name. (Constraints: 1 to 1024 chars) + */ + name: string; + + /** + * (Microsoft only) ID of the parent folder. + */ + parentId?: string; + + /** + * (Google only) The text color of the folder in the hexadecimal format "#0099EE". See Google Defined Values for more information. + */ + textColor?: string; + + /** + * (Google only) The background color of the folder in the hexadecimal format "#0099EE". See Google Defined Values for more information. + */ + backgroundColor?: string; +} + +export type UpdateFolderRequest = Partial; diff --git a/src/nylas.ts b/src/nylas.ts index 7e88d752..731206c9 100644 --- a/src/nylas.ts +++ b/src/nylas.ts @@ -9,6 +9,7 @@ import { Messages } from './resources/messages.js'; import { Drafts } from './resources/drafts.js'; import { Threads } from './resources/threads.js'; import { Connectors } from './resources/connectors.js'; +import { Folders } from './resources/folders.js'; /** * The entry point to the Node SDK @@ -53,6 +54,10 @@ export default class Nylas { * Access the Webhooks API */ public webhooks: Webhooks; + /** + * Access the Folders API + */ + public folders: Folders; /** * The configured API client @@ -79,6 +84,7 @@ export default class Nylas { this.messages = new Messages(this.apiClient); this.threads = new Threads(this.apiClient); this.webhooks = new Webhooks(this.apiClient); + this.folders = new Folders(this.apiClient); return this; } diff --git a/src/resources/folders.ts b/src/resources/folders.ts new file mode 100644 index 00000000..539e7ed1 --- /dev/null +++ b/src/resources/folders.ts @@ -0,0 +1,156 @@ +import { Overrides } from '../config.js'; +import { + Folder, + CreateFolderRequest, + UpdateFolderRequest, +} from '../models/folders.js'; +import { + NylasDeleteResponse, + NylasResponse, + NylasListResponse, +} from '../models/response.js'; +import { Resource, AsyncListResponse } from './resource.js'; + +/** + * The parameters for the {@link Folders.list} method + * @property identifier The identifier of the grant to act upon + */ +interface ListFoldersParams { + identifier: string; +} + +/** + * The parameters for the {@link Folders.find} method + * @property identifier The identifier of the grant to act upon + * @property folderId The id of the Folder to retrieve + */ +interface FindFolderParams { + identifier: string; + folderId: string; +} + +/** + * The parameters for the {@link Folders.create} method + * @property identifier The identifier of the grant to act upon + * @property requestBody The request body to create a folder + */ +interface CreateFolderParams { + identifier: string; + requestBody: CreateFolderRequest; +} + +/** + * The parameters for the {@link Folders.update} method + * @property identifier The identifier of the grant to act upon + * @property folderId The id of the Folder to update + * @property requestBody The request body to update a folder + */ +interface UpdateFolderParams { + identifier: string; + folderId: string; + requestBody: UpdateFolderRequest; +} + +/** + * The parameters for the {@link Folders.destroy} method + * @property identifier The identifier of the grant to act upon + * @property folderId The id of the Folder to delete + */ +interface DestroyFolderParams { + identifier: string; + folderId: string; +} + +/** + * Nylas Folder API + * + * Email providers use folders to store and organize email messages. Examples of common system folders include Inbox, Sent, Drafts, etc. + * + * If your team is migrating from Nylas APIv2, there were previously two separate endpoints for interacting with Folders (Microsoft) and Labels (Google). + * In Nylas API v3, these endpoints are consolidated under Folders. + * + * To simplify the developer experience, Nylas uses the same folders commands to manage both folders and labels, using the folder_id key to refer to the folder's ID on the provider. + * The API also exposes provider-specific fields such as background_color (Google only). + * + * Depending on the provider (Google, some IMAP providers, etc.), a message can be contained in more than one folder. + */ +export class Folders extends Resource { + /** + * Return all Folders + * @return A list of folders + */ + public list({ + identifier, + overrides, + }: ListFoldersParams & Overrides): AsyncListResponse< + NylasListResponse + > { + return super._list>({ + overrides, + path: `/v3/grants/${identifier}/folders`, + }); + } + + /** + * Return a Folder + * @return The folder + */ + public find({ + identifier, + folderId, + overrides, + }: FindFolderParams & Overrides): Promise> { + return super._find({ + path: `/v3/grants/${identifier}/folders/${folderId}`, + overrides, + }); + } + + /** + * Create a Folder + * @return The created folder + */ + public create({ + identifier, + requestBody, + overrides, + }: CreateFolderParams & Overrides): Promise> { + return super._create({ + path: `/v3/grants/${identifier}/folders`, + requestBody, + overrides, + }); + } + + /** + * Update a Folder + * @return The updated Folder + */ + public update({ + identifier, + folderId, + requestBody, + overrides, + }: UpdateFolderParams & Overrides): Promise> { + return super._update({ + path: `/v3/grants/${identifier}/folders/${folderId}`, + requestBody, + overrides, + }); + } + + /** + * Delete a Folder + * @return The deleted Folder + */ + public destroy({ + identifier, + folderId, + overrides, + }: DestroyFolderParams & Overrides): Promise { + return super._destroy({ + path: `/v3/grants/${identifier}/folders/${folderId}`, + overrides, + }); + } +} diff --git a/tests/nylas.spec.ts b/tests/nylas.spec.ts index 058a81ce..b5127395 100644 --- a/tests/nylas.spec.ts +++ b/tests/nylas.spec.ts @@ -20,6 +20,7 @@ describe('Nylas', () => { expect(nylas.calendars.constructor.name).toBe('Calendars'); expect(nylas.events.constructor.name).toBe('Events'); expect(nylas.webhooks.constructor.name).toBe('Webhooks'); + expect(nylas.folders.constructor.name).toBe('Folders'); }); it('should configure the apiClient', () => { diff --git a/tests/resources/folders.spec.ts b/tests/resources/folders.spec.ts new file mode 100644 index 00000000..017802af --- /dev/null +++ b/tests/resources/folders.spec.ts @@ -0,0 +1,129 @@ +import APIClient from '../../src/apiClient'; +import { Folders } from '../../src/resources/folders'; +jest.mock('../../src/apiClient'); + +describe('Folders', () => { + let apiClient: jest.Mocked; + let folders: Folders; + + beforeAll(() => { + apiClient = new APIClient({ + apiKey: 'apiKey', + apiUri: 'https://test.api.nylas.com', + timeout: 30, + }) as jest.Mocked; + + folders = new Folders(apiClient); + apiClient.request.mockResolvedValue({}); + }); + + describe('list', () => { + it('should call apiClient.request with the correct params', async () => { + await folders.list({ + identifier: 'id123', + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + + expect(apiClient.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/v3/grants/id123/folders', + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + }); + }); + + describe('find', () => { + it('should call apiClient.request with the correct params', async () => { + await folders.find({ + identifier: 'id123', + folderId: 'folder123', + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + + expect(apiClient.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/v3/grants/id123/folders/folder123', + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + }); + }); + + describe('create', () => { + it('should call apiClient.request with the correct params', async () => { + await folders.create({ + identifier: 'id123', + requestBody: { + name: 'My Folder', + }, + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + + expect(apiClient.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/v3/grants/id123/folders', + body: { + name: 'My Folder', + }, + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + }); + }); + + describe('update', () => { + it('should call apiClient.request with the correct params', async () => { + await folders.update({ + identifier: 'id123', + folderId: 'folder123', + requestBody: { + name: 'Updated Folder', + }, + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + + expect(apiClient.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/v3/grants/id123/folders/folder123', + body: { + name: 'Updated Folder', + }, + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + }); + }); + + describe('destroy', () => { + it('should call apiClient.request with the correct params', async () => { + await folders.destroy({ + identifier: 'id123', + folderId: 'folder123', + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + + expect(apiClient.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/v3/grants/id123/folders/folder123', + overrides: { + apiUri: 'https://test.api.nylas.com', + }, + }); + }); + }); +});