Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/models/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,28 @@ export interface CleanMessagesResponse extends Message {
*/
conversation: string;
}

/**
* Interface representing a request to send a message using raw MIME format.
*/
export interface SendMimeMessageRequest {
/**
* The raw MIME content of the message to send.
*/
mime: string;
/**
* Optional metadata to attach to the message.
* Defaults to empty string if not provided.
*/
metadata?: string;
}

/**
* Interface representing the query parameters for sending a MIME message.
*/
export interface SendMimeMessageQueryParams {
/**
* The type of message being sent. Must be 'mime' for MIME messages.
*/
type: 'mime';
}
52 changes: 52 additions & 0 deletions src/resources/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
Message,
ScheduledMessage,
ScheduledMessagesList,
SendMimeMessageRequest,
SendMimeMessageQueryParams,
StopScheduledMessageResponse,
UpdateMessageRequest,
} from '../models/messages.js';
Expand Down Expand Up @@ -85,6 +87,18 @@ export interface SendMessageParams {
requestBody: SendMessageRequest;
}

/**
* The parameters for the {@link Messages.sendMime} method
* @property identifier The identifier of the grant to act upon
* @property requestBody The MIME message to send
* @property queryParams The query parameters for the MIME send request
*/
export interface SendMimeMessageParams {
identifier: string;
requestBody: SendMimeMessageRequest;
queryParams: SendMimeMessageQueryParams;
}

/**
* The parameters for the {@link Messages.listScheduledMessages} method
* @property identifier The identifier of the grant to act upon
Expand Down Expand Up @@ -264,6 +278,44 @@ export class Messages extends Resource {
return this.apiClient.request(requestOptions);
}

/**
* Send an email using raw MIME format
* @return The sent message
*/
public async sendMime({
identifier,
requestBody,
queryParams,
overrides,
}: SendMimeMessageParams & Overrides): Promise<NylasResponse<Message>> {
const path = makePathParams('/v3/grants/{identifier}/messages/send', {
identifier,
});

// Create FormData for MIME message
const FD = require('form-data');
const FormDataConstructor = FD.default || FD;
const form: FormData = new FormDataConstructor();

// Add MIME content
form.append('mime', requestBody.mime);

// Add metadata (defaults to empty string if not provided)
if (requestBody.metadata) {
form.append('metadata', requestBody.metadata);
}

const requestOptions: RequestOptionsParams = {
method: 'POST',
path,
form,
queryParams,
overrides,
};

return this.apiClient.request(requestOptions);
}

/**
* Retrieve your scheduled messages
* @return A list of scheduled messages
Expand Down
123 changes: 123 additions & 0 deletions tests/resources/messages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ describe('Messages', () => {
apiClient.request.mockResolvedValue({});
});

beforeEach(() => {
jest.clearAllMocks();
apiClient.request.mockResolvedValue({});
});

describe('list', () => {
it('should call apiClient.request with the correct params', async () => {
await messages.list({
Expand Down Expand Up @@ -482,6 +487,124 @@ describe('Messages', () => {
});
});

describe('sendMime', () => {
it('should call apiClient.request with the correct params for MIME message', async () => {
const mimeContent = `MIME-Version: 1.0
Message-ID: <test@example.com>
Subject: Test MIME Message
From: sender@example.com
To: recipient@example.com
Content-Type: text/plain; charset="UTF-8"

This is a test MIME message.`;

await messages.sendMime({
identifier: 'id123',
requestBody: {
mime: mimeContent,
metadata: 'test-metadata',
},
queryParams: {
type: 'mime',
},
overrides: {
apiUri: 'https://test.api.nylas.com',
headers: { override: 'bar' },
},
});

const capturedRequest =
apiClient.request.mock.calls[
apiClient.request.mock.calls.length - 1
][0];
expect(capturedRequest.method).toEqual('POST');
expect(capturedRequest.path).toEqual('/v3/grants/id123/messages/send');
expect(capturedRequest.queryParams).toEqual({ type: 'mime' });
expect(capturedRequest.overrides).toEqual({
apiUri: 'https://test.api.nylas.com',
headers: { override: 'bar' },
});

// Check that form data was created correctly
const formData = (
capturedRequest.form as any as MockedFormData
)._getAppendedData();
expect(formData.mime).toEqual(mimeContent);
expect(formData.metadata).toEqual('test-metadata');
});

it('should use empty string for metadata when not provided', async () => {
const mimeContent = `MIME-Version: 1.0
Subject: Test MIME Message
From: sender@example.com
To: recipient@example.com
Content-Type: text/plain; charset="UTF-8"

This is a test MIME message.`;

await messages.sendMime({
identifier: 'id123',
requestBody: {
mime: mimeContent,
},
queryParams: {
type: 'mime',
},
});

const capturedRequest =
apiClient.request.mock.calls[
apiClient.request.mock.calls.length - 1
][0];
const formData = (
capturedRequest.form as any as MockedFormData
)._getAppendedData();
expect(formData.mime).toEqual(mimeContent);
});

it('should handle complex MIME content with multipart boundaries', async () => {
const mimeContent = `MIME-Version: 1.0
Message-ID: <complex@example.com>
Subject: Complex MIME Message
From: sender@example.com
To: recipient@example.com
Content-Type: multipart/alternative; boundary="boundary_123"

--boundary_123
Content-Type: text/plain; charset="UTF-8"

Plain text content

--boundary_123
Content-Type: text/html; charset="UTF-8"

<div>HTML content</div>

--boundary_123--`;

await messages.sendMime({
identifier: 'id123',
requestBody: {
mime: mimeContent,
metadata: 'complex-mime-test',
},
queryParams: {
type: 'mime',
},
});

const capturedRequest =
apiClient.request.mock.calls[
apiClient.request.mock.calls.length - 1
][0];
const formData = (
capturedRequest.form as any as MockedFormData
)._getAppendedData();
expect(formData.mime).toEqual(mimeContent);
expect(formData.metadata).toEqual('complex-mime-test');
});
});

describe('scheduledMessages', () => {
it('listing should call apiClient.request with the correct params', async () => {
await messages.listScheduledMessages({
Expand Down
Loading