diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000..aac0e91 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,7 @@ +paths: + - 'src' +paths-ignore: + - 'src/__tests__/**/*.js' + - 'src/__tests__/**/*.ts' + - 'src/__tests__/**/*.jsx' + - 'src/__tests__/**/*.tsx' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7038560..7dfb1db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,8 +5,10 @@ on: push jobs: lint: runs-on: ubuntu-latest - steps: + - uses: github/codeql-action/init@v3 + with: + config-file: ./.github/codeql/codeql-config.yml - uses: actions/checkout@v2 - uses: actions/setup-node@v3 with: diff --git a/README.md b/README.md index 7c227fc..0c156d8 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,6 @@ This NPM package offers integration with the [official API](https://api-docs.mai Quickly add email sending functionality to your Node.js application with Mailtrap. -## Compatibility with previous releases - -Versions of this package up to 2.0.2 were an [unofficial client](https://github.com/vchin/mailtrap-client) developed by [@vchin](https://github.com/vchin). Package version 3 is a completely new package. It is still under development and does not support the testing API yet. Please continue using version 2 if you need access to the testing API. - ## Installation Use yarn or npm to install the package: @@ -122,3 +118,7 @@ The package is available as open source under the terms of the [MIT License](htt ## Code of Conduct Everyone interacting in the Mailtrap project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md). + +## Compatibility with previous releases + +Versions of this package up to 2.0.2 were an [unofficial client](https://github.com/vchin/mailtrap-client) developed by [@vchin](https://github.com/vchin). Package version 3 is a completely new package. diff --git a/examples/bulk/send-mail.ts b/examples/bulk/send-mail.ts index 643373f..ae386b5 100644 --- a/examples/bulk/send-mail.ts +++ b/examples/bulk/send-mail.ts @@ -12,9 +12,9 @@ const TOKEN = ""; const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; -const client = new MailtrapClient({ token: TOKEN }); +const client = new MailtrapClient({ token: TOKEN, bulk: true }); -client.bulk.send({ +client.send({ from: { name: "Mailtrap Test", email: SENDER_EMAIL }, to: [{ email: RECIPIENT_EMAIL }], subject: "Hello from Mailtrap!", diff --git a/examples/bulk/transport.ts b/examples/bulk/transport.ts new file mode 100644 index 0000000..1be49e6 --- /dev/null +++ b/examples/bulk/transport.ts @@ -0,0 +1,60 @@ +import { readFileSync } from "fs"; +import Nodemailer from "nodemailer"; +import { MailtrapTransport } from "mailtrap" + +/** + * For this example, you need to have ready-to-use sending domain or, + * a Demo domain that allows sending emails to your own account email. + * @see https://help.mailtrap.io/article/69-sending-domain-setup + */ + +const TOKEN = "" +const SENDER_EMAIL = ""; +const RECIPIENT_EMAIL = ""; + +const transport = Nodemailer.createTransport(MailtrapTransport({ + token: TOKEN, + bulk: true +})) + +transport.sendMail({ + text: "Welcome to Mailtrap Sending!", + to: { + address: RECIPIENT_EMAIL, + name: "John Doe" + }, + from: { + address: SENDER_EMAIL, + name: "Mailtrap Test" + }, + subject: "Hello from Mailtrap!", + html: ` + + + + + + +
+

Congrats for sending test email with Mailtrap!

+

Inspect it using the tabs you see above and learn how this email can be improved.

+ Inspect with Tabs +

Now send your email using our fake SMTP server and integration of your choice!

+

Good luck! Hope it works.

+
+ + + + +`, + attachments: [ + { + filename: "welcome.png", + content: readFileSync("./welcome.png"), + }, + ], +}).then(console.log) + .catch(console.error) diff --git a/examples/general/account-accesses.ts b/examples/general/account-accesses.ts index cfb0208..96312a2 100644 --- a/examples/general/account-accesses.ts +++ b/examples/general/account-accesses.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" const TOKEN = ""; const TEST_INBOX_ID = "" diff --git a/examples/general/accounts.ts b/examples/general/accounts.ts index 0b1efbc..73b795f 100644 --- a/examples/general/accounts.ts +++ b/examples/general/accounts.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" const TOKEN = ""; const TEST_INBOX_ID = "" diff --git a/examples/general/permissions.ts b/examples/general/permissions.ts index 94728d4..04775b0 100644 --- a/examples/general/permissions.ts +++ b/examples/general/permissions.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" const TOKEN = ""; const TEST_INBOX_ID = "" diff --git a/examples/sending/transport.ts b/examples/sending/transport.ts index 847516a..c347251 100644 --- a/examples/sending/transport.ts +++ b/examples/sending/transport.ts @@ -13,10 +13,9 @@ const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; const transport = Nodemailer.createTransport(MailtrapTransport({ - token: TOKEN + token: TOKEN, })) -// Note: 'sandbox: true' can be passed for making requests to Testing API transport.sendMail({ text: "Welcome to Mailtrap Sending!", to: { diff --git a/examples/testing/attachments.ts b/examples/testing/attachments.ts index b580ee9..51eaed2 100644 --- a/examples/testing/attachments.ts +++ b/examples/testing/attachments.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" const TOKEN = ""; const TEST_INBOX_ID = "" diff --git a/examples/testing/inboxes.ts b/examples/testing/inboxes.ts index 7e147cb..fd30a1e 100644 --- a/examples/testing/inboxes.ts +++ b/examples/testing/inboxes.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" const TOKEN = ""; const TEST_INBOX_ID = "" diff --git a/examples/testing/messages.ts b/examples/testing/messages.ts index 8a6c5b2..1295a68 100644 --- a/examples/testing/messages.ts +++ b/examples/testing/messages.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" const TOKEN = ""; const TEST_INBOX_ID = "" diff --git a/examples/testing/send-mail.ts b/examples/testing/send-mail.ts new file mode 100644 index 0000000..ca40e73 --- /dev/null +++ b/examples/testing/send-mail.ts @@ -0,0 +1,25 @@ +import { MailtrapClient } from "mailtrap" + +/** + * For this example, you need to have ready-to-use sending domain or, + * a Demo domain that allows sending emails to your own account email. + * @see https://help.mailtrap.io/article/69-sending-domain-setup + */ + +", @see https://help.mailtrap.io/article/69-sending-domain-setup#Demo-Domain--oYOU5" + +const TOKEN = ""; +const TEST_INBOX_ID = "" +const SENDER_EMAIL = ""; +const RECIPIENT_EMAIL = ""; + +const client = new MailtrapClient({ token: TOKEN, sandbox: true, testInboxId: TEST_INBOX_ID }); + +client.send({ + from: { name: "Mailtrap Test", email: SENDER_EMAIL }, + to: [{ email: RECIPIENT_EMAIL }], + subject: "Hello from Mailtrap!", + text: "Welcome to Mailtrap Sending!", +}) +.then(console.log) +.catch(console.error); diff --git a/examples/testing/template.ts b/examples/testing/template.ts index 4e71b6f..e39ff57 100644 --- a/examples/testing/template.ts +++ b/examples/testing/template.ts @@ -1,4 +1,4 @@ -import { MailtrapClient } from "../../src" +import { MailtrapClient } from "mailtrap" /** * For this example to work, you need to set up a sending domain, diff --git a/examples/testing/transport.ts b/examples/testing/transport.ts new file mode 100644 index 0000000..1a8abf1 --- /dev/null +++ b/examples/testing/transport.ts @@ -0,0 +1,62 @@ +import { readFileSync } from "fs"; +import Nodemailer from "nodemailer"; +import { MailtrapTransport } from "mailtrap" + +/** + * For this example, you need to have ready-to-use sending domain or, + * a Demo domain that allows sending emails to your own account email. + * @see https://help.mailtrap.io/article/69-sending-domain-setup + */ + +const TOKEN = "" +const TEST_INBOX_ID = "" +const SENDER_EMAIL = ""; +const RECIPIENT_EMAIL = ""; + +const transport = Nodemailer.createTransport(MailtrapTransport({ + token: TOKEN, + testInboxId: TEST_INBOX_ID, + sandbox: true +})) + +transport.sendMail({ + text: "Welcome to Mailtrap Sending!", + to: { + address: RECIPIENT_EMAIL, + name: "John Doe" + }, + from: { + address: SENDER_EMAIL, + name: "Mailtrap Test" + }, + subject: "Hello from Mailtrap!", + html: ` + + + + + + +
+

Congrats for sending test email with Mailtrap!

+

Inspect it using the tabs you see above and learn how this email can be improved.

+ Inspect with Tabs +

Now send your email using our fake SMTP server and integration of your choice!

+

Good luck! Hope it works.

+
+ + + + +`, + attachments: [ + { + filename: "welcome.png", + content: readFileSync("./welcome.png"), + }, + ], +}).then(console.log) + .catch(console.error) diff --git a/src/__tests__/lib/api/Bulk.test.ts b/src/__tests__/lib/api/Bulk.test.ts deleted file mode 100644 index a146143..0000000 --- a/src/__tests__/lib/api/Bulk.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -import axios from "axios"; -import AxiosMockAdapter from "axios-mock-adapter"; - -import BulkAPI from "../../../lib/api/Bulk"; -import handleSendingError from "../../../lib/axios-logger"; -import MailtrapError from "../../../lib/MailtrapError"; - -import CONFIG from "../../../config"; - -const { CLIENT_SETTINGS } = CONFIG; -const { BULK_ENDPOINT } = CLIENT_SETTINGS; - -describe("lib/api/Bulk: ", () => { - let mock: AxiosMockAdapter; - const bulkAPI = new BulkAPI(axios); - - describe("class Testing(): ", () => { - describe("init: ", () => { - it("initalizes with all necessary params.", () => { - expect(bulkAPI).toHaveProperty("send"); - }); - }); - - beforeAll(() => { - /** - * Init Axios interceptors for handling response.data, errors. - */ - axios.interceptors.response.use( - (response) => response.data, - handleSendingError - ); - mock = new AxiosMockAdapter(axios); - }); - - afterEach(() => { - mock.reset(); - }); - - describe("send(): ", () => { - it("successfully sends email.", async () => { - const endpoint = `${BULK_ENDPOINT}/api/send`; - const expectedResponseData = { - success: true, - message_ids: ["0c7fd939-02cf-11ed-88c2-0a58a9feac02"], - }; - mock.onPost(endpoint).reply(200, expectedResponseData); - - const emailData = { - from: { - email: "sender.mock@email.com", - name: "sender", - }, - to: [ - { - email: "recipient.mock@email.com", - name: "recipient", - }, - ], - subject: "mock-subject", - text: "Mock text", - html: "
Mock text
", - }; - - const result = await bulkAPI.send(emailData); - - expect(mock.history.post[0].url).toEqual(endpoint); - expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); - expect(result).toEqual(expectedResponseData); - }); - - it("handles an API error.", async () => { - const responseData = { - success: false, - errors: ["mock-error-1", "mock-error-2"], - }; - - const endpoint = `${BULK_ENDPOINT}/api/send`; - - mock.onPost(endpoint).reply(400, responseData); - - const emailData = { - from: { - email: "sender.mock@email.com", - name: "sender", - }, - to: [ - { - email: "recipient.mock@email.com", - name: "recipient", - }, - ], - subject: "mock-subject", - text: "Mock text", - html: "
Mock text
", - }; - - const expectedErrorMessage = responseData.errors.join(","); - - expect.assertions(3); - - try { - await bulkAPI.send(emailData); - } catch (error) { - expect(mock.history.post[0].url).toEqual(endpoint); - expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); - - if (error instanceof Error) { - expect(error.message).toEqual(expectedErrorMessage); - } - } - }); - - it("handles an HTTP transport error.", async () => { - const emailData = { - from: { - email: "sender.mock@email.com", - name: "sender", - }, - to: [ - { - email: "recipient.mock@email.com", - name: "recipient", - }, - ], - subject: "mock-subject", - text: "Mock text", - html: "
Mock text
", - }; - - const expectedErrorMessage = "Request failed with status code 404"; - - expect.assertions(2); - - try { - await bulkAPI.send(emailData); - } catch (error) { - expect(error).toBeInstanceOf(MailtrapError); - - if (error instanceof MailtrapError) { - expect(error.message).toEqual(expectedErrorMessage); - } - } - }); - }); - }); -}); diff --git a/src/__tests__/lib/api/Testing.test.ts b/src/__tests__/lib/api/Testing.test.ts index bc4a776..0ecf24d 100644 --- a/src/__tests__/lib/api/Testing.test.ts +++ b/src/__tests__/lib/api/Testing.test.ts @@ -1,151 +1,19 @@ import axios from "axios"; -import AxiosMockAdapter from "axios-mock-adapter"; import Testing from "../../../lib/api/Testing"; -import handleSendingError from "../../../lib/axios-logger"; - -import CONFIG from "../../../config"; -import MailtrapError from "../../../lib/MailtrapError"; - -const { CLIENT_SETTINGS } = CONFIG; -const { TESTING_ENDPOINT } = CLIENT_SETTINGS; describe("lib/api/Testing: ", () => { - let mock: AxiosMockAdapter; const testInboxId = 100; const testingAPI = new Testing(axios, testInboxId); describe("class Testing(): ", () => { describe("init: ", () => { it("initalizes with all necessary params.", () => { - expect(testingAPI).toHaveProperty("send"); expect(testingAPI).toHaveProperty("projects"); expect(testingAPI).toHaveProperty("inboxes"); expect(testingAPI).toHaveProperty("messages"); expect(testingAPI).toHaveProperty("attachments"); }); }); - - beforeAll(() => { - /** - * Init Axios interceptors for handling response.data, errors. - */ - axios.interceptors.response.use( - (response) => response.data, - handleSendingError - ); - mock = new AxiosMockAdapter(axios); - }); - - afterEach(() => { - mock.reset(); - }); - - describe("send(): ", () => { - it("successfully sends email.", async () => { - const endpoint = `${TESTING_ENDPOINT}/api/send/${testInboxId}`; - const expectedResponseData = { - success: true, - message_ids: ["0c7fd939-02cf-11ed-88c2-0a58a9feac02"], - }; - mock.onPost(endpoint).reply(200, expectedResponseData); - - const emailData = { - from: { - email: "sender.mock@email.com", - name: "sender", - }, - to: [ - { - email: "recipient.mock@email.com", - name: "recipient", - }, - ], - subject: "mock-subject", - text: "Mock text", - html: "
Mock text
", - }; - - const result = await testingAPI.send(emailData); - - expect(mock.history.post[0].url).toEqual(endpoint); - expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); - expect(result).toEqual(expectedResponseData); - }); - - it("handles an API error.", async () => { - const responseData = { - success: false, - errors: ["mock-error-1", "mock-error-2"], - }; - - const endpoint = `${TESTING_ENDPOINT}/api/send/${testInboxId}`; - - mock.onPost(endpoint).reply(400, responseData); - - const emailData = { - from: { - email: "sender.mock@email.com", - name: "sender", - }, - to: [ - { - email: "recipient.mock@email.com", - name: "recipient", - }, - ], - subject: "mock-subject", - text: "Mock text", - html: "
Mock text
", - }; - - const expectedErrorMessage = responseData.errors.join(","); - - expect.assertions(3); - - try { - await testingAPI.send(emailData); - } catch (error) { - expect(mock.history.post[0].url).toEqual(endpoint); - expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); - - if (error instanceof Error) { - expect(error.message).toEqual(expectedErrorMessage); - } - } - }); - - it("handles an HTTP transport error.", async () => { - const emailData = { - from: { - email: "sender.mock@email.com", - name: "sender", - }, - to: [ - { - email: "recipient.mock@email.com", - name: "recipient", - }, - ], - subject: "mock-subject", - text: "Mock text", - html: "
Mock text
", - }; - - const expectedErrorMessage = "Request failed with status code 404"; - - expect.assertions(2); - - try { - await testingAPI.send(emailData); - } catch (error) { - expect(error).toBeInstanceOf(MailtrapError); - - if (error instanceof MailtrapError) { - expect(error.message).toEqual(expectedErrorMessage); - } - } - }); - }); }); }); diff --git a/src/__tests__/lib/mailtrap-client.test.ts b/src/__tests__/lib/mailtrap-client.test.ts index 824fab5..c510081 100644 --- a/src/__tests__/lib/mailtrap-client.test.ts +++ b/src/__tests__/lib/mailtrap-client.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ /* eslint-disable no-console */ import axios, { AxiosError } from "axios"; import AxiosMockAdapter from "axios-mock-adapter"; @@ -6,9 +7,13 @@ import { Mail, MailtrapClient } from "../.."; import MailtrapError from "../../lib/MailtrapError"; import CONFIG from "../../config"; +import TestingAPI from "../../lib/api/Testing"; +import GeneralAPI from "../../lib/api/General"; -const { ERRORS } = CONFIG; -const { TEST_INBOX_ID_MISSING, ACCOUNT_ID_MISSING } = ERRORS; +const { ERRORS, CLIENT_SETTINGS } = CONFIG; +const { TESTING_ENDPOINT, BULK_ENDPOINT, SENDING_ENDPOINT } = CLIENT_SETTINGS; +const { TEST_INBOX_ID_MISSING, ACCOUNT_ID_MISSING, BULK_SANDBOX_INCOMPATIBLE } = + ERRORS; describe("lib/mailtrap-client: ", () => { let mock: AxiosMockAdapter; @@ -27,31 +32,198 @@ describe("lib/mailtrap-client: ", () => { text: "My TEXT", }; - beforeAll(() => { - mock = new AxiosMockAdapter(axios); - }); + describe("send():", () => { + beforeAll(() => { + mock = new AxiosMockAdapter(axios); + }); - afterEach(() => { - mock.reset(); - }); + afterEach(() => { + mock.reset(); + }); + + it("rejects with Mailtrap error, bulk and sanbox modes are incompatible.", async () => { + const client = new MailtrapClient({ + token: "MY_API_TOKEN", + bulk: true, + sandbox: true, + }); + + try { + await client.send(goodMail); + } catch (error) { + expect(error).toEqual(new MailtrapError(BULK_SANDBOX_INCOMPATIBLE)); + } + }); + const testInboxId = 100; + + it("successfully sends testing email.", async () => { + const testingClient = new MailtrapClient({ + token: "MY_API_TOKEN", + sandbox: true, + testInboxId, + }); + const endpoint = `${TESTING_ENDPOINT}/api/send/${testInboxId}`; + const expectedResponseData = { + success: true, + message_ids: ["0c7fd939-02cf-11ed-88c2-0a58a9feac02"], + }; + mock.onPost(endpoint).reply(200, expectedResponseData); + + const emailData = { + from: { + email: "sender.mock@email.com", + name: "sender", + }, + to: [ + { + email: "recipient.mock@email.com", + name: "recipient", + }, + ], + subject: "mock-subject", + text: "Mock text", + html: "
Mock text
", + }; + + const result = await testingClient.send(emailData); + + expect(mock.history.post[0].url).toEqual(endpoint); + expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); + expect(result).toEqual(expectedResponseData); + }); + + it("successfully sends bulk email.", async () => { + const bulkClient = new MailtrapClient({ + token: "MY_API_TOKEN", + bulk: true, + }); + const endpoint = `${BULK_ENDPOINT}/api/send`; + const expectedResponseData = { + success: true, + message_ids: ["0c7fd939-02cf-11ed-88c2-0a58a9feac02"], + }; + mock.onPost(endpoint).reply(200, expectedResponseData); + + const emailData = { + from: { + email: "sender.mock@email.com", + name: "sender", + }, + to: [ + { + email: "recipient.mock@email.com", + name: "recipient", + }, + ], + subject: "mock-subject", + text: "Mock text", + html: "
Mock text
", + }; + + const result = await bulkClient.send(emailData); + + expect(mock.history.post[0].url).toEqual(endpoint); + expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); + expect(result).toEqual(expectedResponseData); + }); + + it("handles an API error.", async () => { + const testingClient = new MailtrapClient({ + token: "MY_API_TOKEN", + sandbox: true, + testInboxId, + }); + const responseData = { + success: false, + errors: ["mock-error-1", "mock-error-2"], + }; + + const endpoint = `${TESTING_ENDPOINT}/api/send/${testInboxId}`; + + mock.onPost(endpoint).reply(400, responseData); + + const emailData = { + from: { + email: "sender.mock@email.com", + name: "sender", + }, + to: [ + { + email: "recipient.mock@email.com", + name: "recipient", + }, + ], + subject: "mock-subject", + text: "Mock text", + html: "
Mock text
", + }; + + const expectedErrorMessage = responseData.errors.join(","); + + expect.assertions(3); + + try { + await testingClient.send(emailData); + } catch (error) { + expect(mock.history.post[0].url).toEqual(endpoint); + expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); + + if (error instanceof Error) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); + + it("handles an HTTP transport error.", async () => { + const testingClient = new MailtrapClient({ + token: "MY_API_TOKEN", + sandbox: true, + testInboxId, + }); + const emailData = { + from: { + email: "sender.mock@email.com", + name: "sender", + }, + to: [ + { + email: "recipient.mock@email.com", + name: "recipient", + }, + ], + subject: "mock-subject", + text: "Mock text", + html: "
Mock text
", + }; + + const expectedErrorMessage = "Request failed with status code 404"; + + expect.assertions(2); + + try { + await testingClient.send(emailData); + } catch (error) { + expect(error).toBeInstanceOf(MailtrapError); + + if (error instanceof MailtrapError) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); - describe("send():", () => { it("sends request to mailtrap api", async () => { const successData = { success: "true", message_ids: ["00000000-00000000-00000000-00000001"], }; + const endpoint = `${SENDING_ENDPOINT}/api/send`; - mock - .onPost("https://send.api.mailtrap.io/api/send") - .reply(200, successData); + mock.onPost(endpoint).reply(200, successData); const client = new MailtrapClient({ token: "MY_API_TOKEN" }); const result = await client.send(goodMail); - expect(mock.history.post[0].url).toEqual( - "https://send.api.mailtrap.io/api/send" - ); + expect(mock.history.post[0].url).toEqual(endpoint); expect(mock.history.post[0].headers).toMatchObject({ Accept: "application/json, text/plain, */*", "Content-Type": "application/json", @@ -158,44 +330,58 @@ describe("lib/mailtrap-client: ", () => { }); describe("get testing(): ", () => { - it("returns testing API object, console warn is called twice.", () => { - const originalWarn = console.warn; - const mockLogger = jest.fn(); - console.warn = mockLogger; - const client = new MailtrapClient({ token: "MY_API_TOKEN" }); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const testingAPI = client.testing; - - expect.assertions(3); + it("rejects with Mailtrap error, when `testInboxId` is missing.", () => { + const client = new MailtrapClient({ + token: "MY_API_TOKEN", + }); - expect(mockLogger).toBeCalledTimes(2); - expect(mockLogger).toBeCalledWith(TEST_INBOX_ID_MISSING); - expect(mockLogger).toBeCalledWith(ACCOUNT_ID_MISSING); + expect.assertions(1); - console.warn = originalWarn; + try { + client.testing; + } catch (error) { + expect(error).toEqual(new MailtrapError(TEST_INBOX_ID_MISSING)); + } }); - it("return testing API object, without calling console warn.", () => { - const originalWarn = console.warn; - const mockLogger = jest.fn(); - console.warn = mockLogger; - const testInboxId = 1; - const accountId = 1; + it("rejects with Mailtrap error, when `accountId` is missing.", () => { const client = new MailtrapClient({ token: "MY_API_TOKEN", - testInboxId, - accountId, + testInboxId: 5, }); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const testingAPI = client.testing; + expect.assertions(1); + + try { + client.testing; + } catch (error) { + expect(error).toEqual(new MailtrapError(ACCOUNT_ID_MISSING)); + } + }); + it("returns testing API object, console warn is called twice.", () => { + const client = new MailtrapClient({ + token: "MY_API_TOKEN", + sandbox: true, + testInboxId: 10, + accountId: 10, + }); expect.assertions(1); - expect(mockLogger).toBeCalledTimes(0); + const testingClient = client.testing; + expect(testingClient).toBeInstanceOf(TestingAPI); + }); + + describe("get general(): ", () => { + it("returns testing API object, console warn is called twice.", () => { + const client = new MailtrapClient({ + token: "MY_API_TOKEN", + }); + expect.assertions(1); - console.warn = originalWarn; + const generalClient = client.general; + expect(generalClient).toBeInstanceOf(GeneralAPI); + }); }); }); }); diff --git a/src/__tests__/lib/normalizer.test.ts b/src/__tests__/lib/normalizer.test.ts index eede3ad..d9dd9e3 100644 --- a/src/__tests__/lib/normalizer.test.ts +++ b/src/__tests__/lib/normalizer.test.ts @@ -124,43 +124,5 @@ describe("lib/normalizer: ", () => { // @ts-ignore cb(null, mailData); }); - - it("checks if testing client is switched.", () => { - expect.assertions(3); - - const mockResponse = { - statusCode: 200, - }; - const mockClient = { - testing: { - send: jest.fn(() => Promise.resolve(mockResponse)), - }, - }; - - const callback = (error: Error, data: SendError) => { - expect(error).toBeNull(); - expect(data).toEqual(mockResponse); - }; - const mailData = { - sandbox: true, - text: "mock-text", - to: { - address: "mock@mail.com", - name: "mock-name", - }, - from: { - address: "mock@mail.com", - name: "mock-name", - }, - subject: "mock-subject", - }; - - // @ts-ignore - const cb = normalizeCallback(mockClient, callback); - - // @ts-ignore - cb(null, mailData); - expect(mockClient.testing.send).toBeCalledTimes(1); - }); }); }); diff --git a/src/config/index.ts b/src/config/index.ts index df9a708..f1b5ad2 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -9,6 +9,7 @@ export default { TEST_INBOX_ID_MISSING: "testInboxId is missing, testing API will not work.", ACCOUNT_ID_MISSING: "accountId is missing, some features of testing API may not work properly.", + BULK_SANDBOX_INCOMPATIBLE: "Bulk mode is not applicable for sandbox API.", }, CLIENT_SETTINGS: { SENDING_ENDPOINT: "https://send.api.mailtrap.io", diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index 2f346c1..a0b9e81 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -8,16 +8,23 @@ import handleSendingError from "./axios-logger"; import GeneralAPI from "./api/General"; import TestingAPI from "./api/Testing"; -import BulkAPI from "./api/Bulk"; import CONFIG from "../config"; import { Mail, SendResponse, MailtrapClientConfig } from "../types/mailtrap"; +import MailtrapError from "./MailtrapError"; const { CLIENT_SETTINGS, ERRORS } = CONFIG; -const { SENDING_ENDPOINT, MAX_REDIRECTS, USER_AGENT, TIMEOUT } = - CLIENT_SETTINGS; -const { TEST_INBOX_ID_MISSING, ACCOUNT_ID_MISSING } = ERRORS; +const { + SENDING_ENDPOINT, + MAX_REDIRECTS, + USER_AGENT, + TIMEOUT, + TESTING_ENDPOINT, + BULK_ENDPOINT, +} = CLIENT_SETTINGS; +const { TEST_INBOX_ID_MISSING, ACCOUNT_ID_MISSING, BULK_SANDBOX_INCOMPATIBLE } = + ERRORS; /** * Mailtrap client class. Initializes instance with available methods. @@ -29,16 +36,20 @@ export default class MailtrapClient { private accountId?: number; - private testingAPI: TestingAPI; + private bulk: boolean; - public general: GeneralAPI; - - public bulk: BulkAPI; + private sandbox: boolean; /** * Initalizes axios instance with Mailtrap params. */ - constructor({ token, testInboxId, accountId }: MailtrapClientConfig) { + constructor({ + token, + testInboxId, + accountId, + bulk = false, + sandbox = false, + }: MailtrapClientConfig) { this.axios = axios.create({ httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), @@ -51,50 +62,69 @@ export default class MailtrapClient { timeout: TIMEOUT, }); - /** - * Init Axios interceptors for handling response.data, errors. - */ + /** Init Axios interceptors for handling response.data, errors. */ this.axios.interceptors.response.use( (response) => response.data, handleSendingError ); + this.testInboxId = testInboxId; this.accountId = accountId; - - /** - * Initialize APIs. - */ - this.testingAPI = new TestingAPI( - this.axios, - this.testInboxId, - this.accountId - ); - this.general = new GeneralAPI(this.axios, this.accountId); - this.bulk = new BulkAPI(this.axios); + this.bulk = bulk; + this.sandbox = sandbox; } /** - * Getter for testing API. Warns if some of the required keys are missing. + * Getter for Testing API. Warns if some of the required keys are missing. */ get testing() { if (!this.testInboxId) { - // eslint-disable-next-line no-console - console.warn(TEST_INBOX_ID_MISSING); + throw new MailtrapError(TEST_INBOX_ID_MISSING); } if (!this.accountId) { - // eslint-disable-next-line no-console - console.warn(ACCOUNT_ID_MISSING); + throw new MailtrapError(ACCOUNT_ID_MISSING); + } + + return new TestingAPI(this.axios, this.accountId); + } + + /** + * Getter for General API. + */ + get general() { + return new GeneralAPI(this.axios, this.accountId); + } + + /** + * Returns configured host. Checks if `bulk` and `sandbox` modes are activated simultaneously, + * then reject with Mailtrap Error. + * Otherwise returns appropriate host url. + */ + private determineHost() { + let host; + + if (this.bulk && this.sandbox) { + throw new MailtrapError(BULK_SANDBOX_INCOMPATIBLE); + } else if (this.sandbox) { + host = TESTING_ENDPOINT; + } else if (this.bulk) { + host = BULK_ENDPOINT; + } else { + host = SENDING_ENDPOINT; } - return this.testingAPI; + return host; } /** * Sends mail with given `mail` params. If there is error, rejects with `MailtrapError`. */ public async send(mail: Mail): Promise { - const url = `${SENDING_ENDPOINT}/api/send`; + const host = this.determineHost(); + const url = `${host}/api/send${ + this.testInboxId ? `/${this.testInboxId}` : "" + }`; const preparedMail = encodeMailBuffers(mail); return this.axios.post(url, preparedMail); diff --git a/src/lib/api/Bulk.ts b/src/lib/api/Bulk.ts deleted file mode 100644 index 03f1e54..0000000 --- a/src/lib/api/Bulk.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { AxiosInstance } from "axios"; - -import encodeMailBuffers from "../mail-buffer-encoder"; - -import CONFIG from "../../config"; - -import { Mail, SendResponse } from "../../types/mailtrap"; - -const { CLIENT_SETTINGS } = CONFIG; -const { BULK_ENDPOINT } = CLIENT_SETTINGS; - -export default class BulkAPI { - private client: AxiosInstance; - - constructor(client: AxiosInstance) { - this.client = client; - } - - public async send(mail: Mail): Promise { - const url = `${BULK_ENDPOINT}/api/send`; - const preparedMail = encodeMailBuffers(mail); - - return this.client.post(url, preparedMail); - } -} diff --git a/src/lib/api/Testing.ts b/src/lib/api/Testing.ts index 647ccc9..bf2b58e 100644 --- a/src/lib/api/Testing.ts +++ b/src/lib/api/Testing.ts @@ -5,20 +5,9 @@ import InboxesApi from "./resources/Inboxes"; import MessagesApi from "./resources/Messages"; import AttachmentsApi from "./resources/Attachments"; -import encodeMailBuffers from "../mail-buffer-encoder"; - -import CONFIG from "../../config"; - -import { Mail, SendResponse } from "../../types/mailtrap"; - -const { CLIENT_SETTINGS } = CONFIG; -const { TESTING_ENDPOINT } = CLIENT_SETTINGS; - export default class TestingAPI { private client: AxiosInstance; - private testInboxId?: number; - private accountId?: number; public projects: ProjectsApi; @@ -29,20 +18,12 @@ export default class TestingAPI { public attachments: AttachmentsApi; - constructor(client: AxiosInstance, testInboxId?: number, accountId?: number) { + constructor(client: AxiosInstance, accountId?: number) { this.client = client; this.accountId = accountId; - this.testInboxId = testInboxId; this.projects = new ProjectsApi(this.client, this.accountId); this.inboxes = new InboxesApi(this.client, this.accountId); this.messages = new MessagesApi(this.client, this.accountId); this.attachments = new AttachmentsApi(this.client, this.accountId); } - - public async send(mail: Mail): Promise { - const url = `${TESTING_ENDPOINT}/api/send/${this.testInboxId}`; - const preparedMail = encodeMailBuffers(mail); - - return this.client.post(url, preparedMail); - } } diff --git a/src/lib/normalizer.ts b/src/lib/normalizer.ts index 91021a9..94a984b 100644 --- a/src/lib/normalizer.ts +++ b/src/lib/normalizer.ts @@ -37,12 +37,10 @@ export default function normalizeCallback( }); } - const mailtrapClient = data.sandbox ? client.testing : client; - - return mailtrapClient + return client .send(mail as MailtrapMail) .then((sendResponse) => callback(null, sendResponse)) - .catch((error) => { + .catch((error: any) => { callback(new Error(error), { success: false, errors: [SENDING_FAILED], diff --git a/src/types/mailtrap.ts b/src/types/mailtrap.ts index 59a46b6..2cca7b0 100644 --- a/src/types/mailtrap.ts +++ b/src/types/mailtrap.ts @@ -64,4 +64,6 @@ export type MailtrapClientConfig = { token: string; testInboxId?: number; accountId?: number; + bulk?: boolean; + sandbox?: boolean; }; diff --git a/src/types/transport.ts b/src/types/transport.ts index a6f8a24..5c83062 100644 --- a/src/types/transport.ts +++ b/src/types/transport.ts @@ -13,7 +13,6 @@ type AdditionalFields = { custom_variables?: CustomVariables; template_uuid?: string; template_variables?: TemplateVariables; - sandbox?: boolean | undefined; }; export type NormalizeCallbackData =