Skip to content

Commit

Permalink
Attachments api
Browse files Browse the repository at this point in the history
  • Loading branch information
yifanplanet committed Nov 22, 2023
1 parent 38f075c commit 3908446
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 0 deletions.
36 changes: 36 additions & 0 deletions src/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,40 @@ export default class APIClient {

return this.requestWithResponse(response);
}

async requestRaw(options: RequestOptionsParams): Promise<Buffer> {
const req = this.newRequest(options);
const controller: AbortController = new AbortController();
const timeout = setTimeout(() => {
controller.abort();
throw new NylasSdkTimeoutError(req.url, this.timeout);
}, this.timeout);

const response = await fetch(req, { signal: controller.signal });
clearTimeout(timeout);

if (typeof response === 'undefined') {
throw new Error('Failed to fetch response');
}

// Handle error response
if (response.status > 299) {
const text = await response.text();
let error: Error;
try {
const parsedError = JSON.parse(text);
const camelCaseError = objKeysToCamelCase(parsedError);
error = new NylasApiError(camelCaseError, response.status);
} catch (e) {
throw new Error(
`Received an error but could not parse response from the server: ${text}`
);
}

throw error;
}

// Return the raw buffer
return response.buffer();
}
}
52 changes: 52 additions & 0 deletions src/models/attachments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Interface of an attachment object from Nylas.
*/
export interface Attachment {
/**
* A globally unique object identifier.
* Constraints: Minimum 1 character.
*/
id: string;

/**
* Attachment's name.
* Constraints: Minimum 1 character.
*/
filename: string;

/**
* Attachment's content type.
* Constraints: Minimum 1 character.
*/
contentType: string;

/**
* Grant ID of the Nylas account.
*/
grantId: string;

/**
* If it's an inline attachment.
*/
isInline: boolean;

/**
* Attachment's size in bytes.
*/
size: number;
}

/**
* Interface representing of the query parameters for finding an attachment's metadata.
*/
export interface FindAttachmentQueryParams {
/**
* ID of the message the attachment belongs to.
*/
messageId: string;
}

/**
* Interface representing of the query parameters for downloading an attachment.
*/
export type DownloadAttachmentQueryParams = Partial<FindAttachmentQueryParams>;
6 changes: 6 additions & 0 deletions src/nylas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Drafts } from './resources/drafts.js';
import { Threads } from './resources/threads.js';
import { Connectors } from './resources/connectors.js';
import { Folders } from './resources/folders.js';
import { Attachments } from './resources/attachments.js';

/**
* The entry point to the Node SDK
Expand Down Expand Up @@ -58,6 +59,10 @@ export default class Nylas {
* Access the Folders API
*/
public folders: Folders;
/**
* Access the Attachments API
*/
public attachments: Attachments;

/**
* The configured API client
Expand Down Expand Up @@ -85,6 +90,7 @@ export default class Nylas {
this.threads = new Threads(this.apiClient);
this.webhooks = new Webhooks(this.apiClient);
this.folders = new Folders(this.apiClient);
this.attachments = new Attachments(this.apiClient);

return this;
}
Expand Down
82 changes: 82 additions & 0 deletions src/resources/attachments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Overrides } from '../config.js';
import {
Attachment,
FindAttachmentQueryParams,
DownloadAttachmentQueryParams,
} from '../models/attachments.js';
import { NylasResponse } from '../models/response.js';
import { Resource } from './resource.js';

/**
* @property identifier The ID of the grant to act upon. Use "me" to refer to the grant associated with an access token.
* @property attachmentId The ID of the attachment to act upon.
* @property queryParams The query parameters to include in the request
*/
interface FindAttachmentParams {
identifier: string;
attachmentId: string;
queryParams: FindAttachmentQueryParams;
}

/**
* @property identifier The ID of the grant to act upon. Use "me" to refer to the grant associated with an access token.
* @property attachmentId The ID of the attachment to act upon.
* @property queryParams The query parameters to include in the request
*/
interface DownloadAttachmentParams {
identifier: string;
attachmentId: string;
queryParams: DownloadAttachmentQueryParams;
}

/**
* Nylas Attachments API
*
* The Nylas Attachments API allows you to retrieve metadata and download attachments.
*
* You can use the attachments schema in a Send v3 request to send attachments regardless of the email provider.
* To include attachments in a Send v3 request, all attachments must be base64 encoded and the encoded data must be placed in the content field in the request body.
*
* If you are using draft support, the draft including the attachment is stored on the provider. If you are not using draft support, Nylas stores the attachment.
*
* Attachment size is currently limited by each provider.
* 3MB for Gmail messages
* 10MB for Microsoft messages
*
* You can also send attachments inline in an email message, for example for images that should be displayed in the email body content.
*/
export class Attachments extends Resource {
/**
* Returns an attachment by ID.
* @return The Attachment metadata
*/
public find({
identifier,
attachmentId,
queryParams,
overrides,
}: FindAttachmentParams & Overrides): Promise<NylasResponse<Attachment>> {
return super._find({
path: `/v3/grants/${identifier}/attachments/${attachmentId}`,
queryParams,
overrides,
});
}

/**
* Returns an attachment by ID.
* @return The Attachment file in binary format
*/
public download({
identifier,
attachmentId,
queryParams,
overrides,
}: DownloadAttachmentParams & Overrides): Promise<Buffer> {
return super._getRaw({
path: `/v3/grants/${identifier}/attachments/${attachmentId}/download`,
queryParams,
overrides,
});
}
}
13 changes: 13 additions & 0 deletions src/resources/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ export class Resource {
overrides,
});
}

protected _getRaw({
path,
queryParams,
overrides,
}: FindParams<void>): Promise<Buffer> {
return this.apiClient.requestRaw({
method: 'GET',
path,
queryParams,
overrides,
});
}
}

type ListYieldReturn<T> = T & {
Expand Down
1 change: 1 addition & 0 deletions tests/nylas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('Nylas', () => {
expect(nylas.events.constructor.name).toBe('Events');
expect(nylas.webhooks.constructor.name).toBe('Webhooks');
expect(nylas.folders.constructor.name).toBe('Folders');
expect(nylas.attachments.constructor.name).toBe('Attachments');
});

it('should configure the apiClient', () => {
Expand Down
72 changes: 72 additions & 0 deletions tests/resources/attachments.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import APIClient from '../../src/apiClient';
import { Attachments } from '../../src/resources/attachments';
jest.mock('../src/apiClient');

describe('Attachments', () => {
let apiClient: jest.Mocked<APIClient>;
let attachments: Attachments;

beforeAll(() => {
apiClient = new APIClient({
apiKey: 'apiKey',
apiUri: 'https://test.api.nylas.com',
timeout: 30,
}) as jest.Mocked<APIClient>;

attachments = new Attachments(apiClient);
apiClient.request.mockResolvedValue({});
apiClient.requestRaw.mockResolvedValue(Buffer.from(''));
});

describe('find', () => {
it('should call apiClient.request with the correct params for attachment metadata', async () => {
await attachments.find({
identifier: 'id123',
attachmentId: 'attach123',
queryParams: {
messageId: 'message123',
},
overrides: {
apiUri: 'https://test.api.nylas.com',
},
});

expect(apiClient.request).toHaveBeenCalledWith({
method: 'GET',
path: '/v3/grants/id123/attachments/attach123',
queryParams: {
messageId: 'message123',
},
overrides: {
apiUri: 'https://test.api.nylas.com',
},
});
});
});

describe('download', () => {
it('should call apiClient.requestRaw with the correct params for downloading an attachment', async () => {
await attachments.download({
identifier: 'id123',
attachmentId: 'attach123',
queryParams: {
messageId: 'message123',
},
overrides: {
apiUri: 'https://test.api.nylas.com',
},
});

expect(apiClient.requestRaw).toHaveBeenCalledWith({
method: 'GET',
path: '/v3/grants/id123/attachments/attach123/download',
queryParams: {
messageId: 'message123',
},
overrides: {
apiUri: 'https://test.api.nylas.com',
},
});
});
});
});

0 comments on commit 3908446

Please sign in to comment.