From c151db3d44e9887d20019b38ef8c1f1b0aeec0c6 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 3 Nov 2026 09:14:36 +0100 Subject: [PATCH 1/4] Add Sandbox and Domain names and deprecate the old ones The product now calls Email Sandbox inboxes "sandboxes" and sending domains "domains". Add client.sandbox (with sandboxes), client.domains and the sandboxId option, plus Sandbox and Domain types. Decisions: - Keep client.testing, client.testing.inboxes and its *Inbox methods, client.sendingDomains, testInboxId and the Inbox/SendingDomain types as deprecated subclasses and aliases, so 6.x code keeps working - Warn through process.emitWarning once per old name: getters build a new instance on each access, and no-console lints src/ - Keep the /inboxes and /sending_domains paths and request roots; the new names call the same endpoints - Throw when sandboxId and testInboxId differ instead of picking one - Rename the private sandbox flag to sandboxMode, which collided with the new sandbox getter - Fix examples/sandbox/template.ts (called a missing client.testing.send) and the swapped attachments getList arguments Co-Authored-By: Claude Opus 5.5 --- README.md | 46 ++-- examples/batch/sandbox.ts | 4 +- examples/company-info/everything.ts | 2 +- examples/domains/everything.ts | 58 +++++ examples/email-campaigns/everything.ts | 4 +- examples/general/account-accesses.ts | 3 +- examples/general/accounts.ts | 3 +- examples/general/billing.ts | 3 +- examples/general/permissions.ts | 3 +- examples/marketing-stats/everything.ts | 2 +- examples/sandbox/attachments.ts | 33 +++ examples/sandbox/messages.ts | 41 ++++ examples/{testing => sandbox}/projects.ts | 6 +- examples/sandbox/sandboxes.ts | 35 +++ examples/{testing => sandbox}/send-mail.ts | 4 +- examples/{testing => sandbox}/template.ts | 5 +- examples/{testing => sandbox}/transport.ts | 4 +- examples/sending-domains/everything.ts | 58 ----- examples/testing/attachments.ts | 33 --- examples/testing/inboxes.ts | 35 --- examples/testing/messages.ts | 41 ---- src/__tests__/lib/api/Sandbox.test.ts | 43 ++++ src/__tests__/lib/api/Testing.test.ts | 19 -- .../lib/api/resources/Attachments.test.ts | 18 +- ...SendingDomains.test.ts => Domains.test.ts} | 142 ++++++------ .../lib/api/resources/Messages.test.ts | 99 ++++----- .../{Inboxes.test.ts => Sandboxes.test.ts} | 204 +++++++++++------- src/__tests__/lib/deprecation.test.ts | 35 +++ src/__tests__/lib/mailtrap-client.test.ts | 194 +++++++++++++---- src/config/index.ts | 4 +- src/lib/MailtrapClient.ts | 66 ++++-- src/lib/api/Domains.ts | 27 +++ src/lib/api/Sandbox.ts | 23 ++ src/lib/api/SendingDomains.ts | 33 +-- src/lib/api/Testing.ts | 27 ++- src/lib/api/resources/Attachments.ts | 16 +- src/lib/api/resources/CompanyInfo.ts | 14 +- src/lib/api/resources/Domains.ts | 89 ++++++++ src/lib/api/resources/EmailCampaigns.ts | 2 +- src/lib/api/resources/Inboxes.ts | 128 +++-------- src/lib/api/resources/Messages.ts | 56 ++--- src/lib/api/resources/Permissions.ts | 2 +- src/lib/api/resources/Projects.ts | 4 +- src/lib/api/resources/Sandboxes.ts | 117 ++++++++++ src/lib/api/resources/SendingDomains.ts | 91 +------- src/lib/api/resources/TrackingOptOuts.ts | 2 +- src/lib/deprecation.ts | 15 ++ src/types/api/domains.ts | 51 +++++ src/types/api/email-campaigns.ts | 6 +- src/types/api/inboxes.ts | 40 +--- src/types/api/marketing-stats.ts | 2 +- src/types/api/projects.ts | 4 +- src/types/api/sandboxes.ts | 37 ++++ src/types/api/sending-domains.ts | 65 ++---- src/types/mailtrap.ts | 3 + 55 files changed, 1259 insertions(+), 842 deletions(-) create mode 100644 examples/domains/everything.ts create mode 100644 examples/sandbox/attachments.ts create mode 100644 examples/sandbox/messages.ts rename examples/{testing => sandbox}/projects.ts (70%) create mode 100644 examples/sandbox/sandboxes.ts rename examples/{testing => sandbox}/send-mail.ts (90%) rename examples/{testing => sandbox}/template.ts (80%) rename examples/{testing => sandbox}/transport.ts (95%) delete mode 100644 examples/sending-domains/everything.ts delete mode 100644 examples/testing/attachments.ts delete mode 100644 examples/testing/inboxes.ts delete mode 100644 examples/testing/messages.ts create mode 100644 src/__tests__/lib/api/Sandbox.test.ts delete mode 100644 src/__tests__/lib/api/Testing.test.ts rename src/__tests__/lib/api/resources/{SendingDomains.test.ts => Domains.test.ts} (64%) rename src/__tests__/lib/api/resources/{Inboxes.test.ts => Sandboxes.test.ts} (61%) create mode 100644 src/__tests__/lib/deprecation.test.ts create mode 100644 src/lib/api/Domains.ts create mode 100644 src/lib/api/Sandbox.ts create mode 100644 src/lib/api/resources/Domains.ts create mode 100644 src/lib/api/resources/Sandboxes.ts create mode 100644 src/lib/deprecation.ts create mode 100644 src/types/api/domains.ts create mode 100644 src/types/api/sandboxes.ts diff --git a/README.md b/README.md index 15e9d6c5..05e1a3ed 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Example `.env` variables (or export in shell): ```bash MAILTRAP_API_KEY=your_api_token # https://mailtrap.io/api-tokens MAILTRAP_USE_SANDBOX=true # true/false toggle -MAILTRAP_INBOX_ID=123456 # Only needed for sandbox +MAILTRAP_SANDBOX_ID=123456 # Only needed for sandbox ``` Bootstrap logic: @@ -71,12 +71,12 @@ import { MailtrapClient } from "mailtrap"; const apiKey = process.env.MAILTRAP_API_KEY; const isSandbox = process.env.MAILTRAP_USE_SANDBOX === "true"; -const inboxId = isSandbox ? Number(process.env.MAILTRAP_INBOX_ID) : undefined; // required only for sandbox +const sandboxId = isSandbox ? Number(process.env.MAILTRAP_SANDBOX_ID) : undefined; // required only for sandbox const client = new MailtrapClient({ token: apiKey, sandbox: isSandbox, - testInboxId: inboxId, // undefined is ignored for production + sandboxId, // undefined is ignored for production }); client @@ -119,7 +119,7 @@ const mailtrap = new MailtrapClient({ token: process.env.MAILTRAP_API_KEY, // your API token bulk: false, // set to true for bulk email sending (false by default) sandbox: false, // set to true for sandbox mode (false by default) - testInboxId: undefined, // optional, only for sandbox mode + sandboxId: undefined, // optional, only for sandbox mode }); const welcomeImage = fs.readFileSync(path.join(__dirname, "welcome.png")); @@ -236,23 +236,23 @@ Email API: - Batch send (Bulk) – [`batch/bulk.ts`](examples/bulk/bulk.ts) - Batch send with Template (Transactional) – [`batch/template.ts`](examples/batch/template.ts) - Batch send with Template (Bulk) – [`batch/template.ts`](examples/batch/template.ts) -- Sending domain management CRUD and settings – [`sending-domains/everything.ts`](examples/sending-domains/everything.ts) -- Sending domain company info – [`company-info/everything.ts`](examples/company-info/everything.ts) +- Domain management CRUD and settings – [`domains/everything.ts`](examples/domains/everything.ts) +- Domain company info – [`company-info/everything.ts`](examples/company-info/everything.ts) - Sending stats (aggregated and by domain, category, ESP, date) – [`stats/everything.ts`](examples/stats/everything.ts) - Email logs (list with filters, get by message ID) – [`email-logs/everything.ts`](examples/email-logs/everything.ts) - Webhooks CRUD – [`webhooks/everything.ts`](examples/webhooks/everything.ts) - Verifying webhook signatures – [`webhooks/verify-signature.ts`](examples/webhooks/verify-signature.ts) -Email Sandbox (Testing): +Email Sandbox: -- Send an email (Sandbox) – [`testing/send-mail.ts`](examples/testing/send-mail.ts) -- Send an email with a Template (Sandbox) – [`testing/template.ts`](examples/testing/template.ts) +- Send an email (Sandbox) – [`sandbox/send-mail.ts`](examples/sandbox/send-mail.ts) +- Send an email with a Template (Sandbox) – [`sandbox/template.ts`](examples/sandbox/template.ts) - Batch send (Sandbox) – [`batch/sandbox.ts`](examples/batch/sandbox.ts) - Batch send with Template (Sandbox) – [`batch/sandbox.ts`](examples/batch/sandbox.ts) -- Message management CRUD – [`testing/messages.ts`](examples/testing/messages.ts) -- Inbox management CRUD – [`testing/inboxes.ts`](examples/testing/inboxes.ts) -- Project management CRUD – [`testing/projects.ts`](examples/testing/projects.ts) -- Attachments operations – [`testing/attachments.ts`](examples/testing/attachments.ts) +- Message management CRUD – [`sandbox/messages.ts`](examples/sandbox/messages.ts) +- Sandbox management CRUD – [`sandbox/sandboxes.ts`](examples/sandbox/sandboxes.ts) +- Project management CRUD – [`sandbox/projects.ts`](examples/sandbox/projects.ts) +- Attachments operations – [`sandbox/attachments.ts`](examples/sandbox/attachments.ts) Inbound Email: @@ -304,6 +304,26 @@ Everyone interacting in the Mailtrap project's codebases, issue trackers, chat r 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. +### Renamed APIs + +The SDK now uses the product's names: Email Sandbox inboxes are **sandboxes** and sending domains are **domains**. The old names still work, call the same endpoints, and will be removed in a future major release. Each old option, getter and method emits a `DeprecationWarning` the first time it is used, and the old types are marked `@deprecated`. + +| Old name | New name | +|---|---| +| `testInboxId` config option | `sandboxId` | +| `client.testing` | `client.sandbox` | +| `client.testing.inboxes` | `client.sandbox.sandboxes` | +| `inboxes.getInboxAttributes()` | `sandboxes.getSandboxAttributes()` | +| `inboxes.updateInbox()` | `sandboxes.updateSandbox()` | +| `inboxes.cleanInbox()` | `sandboxes.cleanSandbox()` | +| `client.sendingDomains` | `client.domains` | +| `Inbox`, `UpdateInboxParams` types | `Sandbox`, `UpdateSandboxParams` | +| `SendingDomain`, `SendingDomainPermissions`, `CreateSendingDomainParams`, `UpdateSendingDomainParams`, `SendingDomainsResponse` types | `Domain`, `DomainPermissions`, `CreateDomainParams`, `UpdateDomainParams`, `DomainsResponse` | + +The other methods of `client.testing` and `client.sendingDomains` keep their names. Fields that mirror the API, such as `inbox_id` on messages or `sending_domain_id` on email logs, keep their names too. Passing both `sandboxId` and `testInboxId` with different values throws a `MailtrapError`. + +The warnings carry the code `MAILTRAP_DEPRECATED_NAME`. `node --no-deprecation` silences them, along with every other deprecation warning. + ### Upgrading to 5.0 `client.templates` now calls the paginated `/api/templates` endpoints instead of `/api/email_templates`: diff --git a/examples/batch/sandbox.ts b/examples/batch/sandbox.ts index ac06ff7d..1af24a0f 100644 --- a/examples/batch/sandbox.ts +++ b/examples/batch/sandbox.ts @@ -7,14 +7,14 @@ import { MailtrapClient } from "mailtrap"; */ const TOKEN = ""; -const TEST_INBOX_ID = ""; +const SANDBOX_ID = ""; const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; const client = new MailtrapClient({ token: TOKEN, sandbox: true, - testInboxId: TEST_INBOX_ID + sandboxId: SANDBOX_ID }); client.batchSend({ diff --git a/examples/company-info/everything.ts b/examples/company-info/everything.ts index b34de568..02da30ae 100644 --- a/examples/company-info/everything.ts +++ b/examples/company-info/everything.ts @@ -22,7 +22,7 @@ async function companyInfoFlow() { }); console.log("Created company info:", JSON.stringify(created, null, 2)); - // Get the company info of a sending domain + // Get the company info of a domain const companyInfo = await client.companyInfo.get(DOMAIN_ID); console.log("Company info:", JSON.stringify(companyInfo, null, 2)); diff --git a/examples/domains/everything.ts b/examples/domains/everything.ts new file mode 100644 index 00000000..1426318b --- /dev/null +++ b/examples/domains/everything.ts @@ -0,0 +1,58 @@ +import { MailtrapClient } from "mailtrap"; + +const TOKEN = ""; +const ACCOUNT_ID = ""; + +const client = new MailtrapClient({ + token: TOKEN, + accountId: Number(ACCOUNT_ID), +}); + +async function domainsFlow() { + try { + // Get all domains + const all = await client.domains.getList(); + console.log("All domains:", JSON.stringify(all, null, 2)); + + if (!all.data || all.data.length === 0) { + console.log("No domains found for this account."); + return; + } + + // Get a specific domain + const one = await client.domains.get(all.data[0].id); + console.log("One domain:", JSON.stringify(one, null, 2)); + + // Send setup instructions + const setupResponse = await client.domains.sendSetupInstructions( + all.data[0].id, + "admin@example.com" + ); + console.log("Setup instructions sent"); + + // Create a new domain + const created = await client.domains.create({ + domain_name: "test-domain-" + Date.now() + ".com", + }); + console.log("Created domain:", JSON.stringify(created, null, 2)); + + // Update the settings of the created domain + const updated = await client.domains.update(created.id, { + open_tracking_enabled: true, + click_tracking_enabled: true, + tracking_opt_out_enabled: true, + auto_unsubscribe_link_enabled: false, + inbound_enabled: false, + }); + console.log("Updated domain:", JSON.stringify(updated, null, 2)); + + // Delete the created domain + await client.domains.delete(created.id); + console.log("Domain deleted"); + + } catch (error) { + console.error("Error in domainsFlow:", error instanceof Error ? error.message : String(error)); + } +} + +domainsFlow(); diff --git a/examples/email-campaigns/everything.ts b/examples/email-campaigns/everything.ts index 471da271..11763319 100644 --- a/examples/email-campaigns/everything.ts +++ b/examples/email-campaigns/everything.ts @@ -1,8 +1,8 @@ import { MailtrapClient } from "mailtrap"; const TOKEN = ""; -// ID of a verified sending domain on the account (required to create a campaign), -// as returned by the Sending Domains endpoints. +// ID of a verified domain on the account (required to create a campaign), +// as returned by `client.domains`. const SENDING_DOMAIN_ID = ""; // The Email Campaigns API is token-scoped — no `accountId` is needed. diff --git a/examples/general/account-accesses.ts b/examples/general/account-accesses.ts index 96312a20..1aa68545 100644 --- a/examples/general/account-accesses.ts +++ b/examples/general/account-accesses.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const TEST_INBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const accountAccessesClient = client.general.accountAccesses diff --git a/examples/general/accounts.ts b/examples/general/accounts.ts index 73b795f2..973678c6 100644 --- a/examples/general/accounts.ts +++ b/examples/general/accounts.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const TEST_INBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const accountsClient = client.general.accounts diff --git a/examples/general/billing.ts b/examples/general/billing.ts index 3ebb8c80..dee555e7 100644 --- a/examples/general/billing.ts +++ b/examples/general/billing.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const TEST_INBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const billingClient = client.general.billing diff --git a/examples/general/permissions.ts b/examples/general/permissions.ts index 24d01bbf..72f0081e 100644 --- a/examples/general/permissions.ts +++ b/examples/general/permissions.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const TEST_INBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const permissionsClient = client.general.permissions diff --git a/examples/marketing-stats/everything.ts b/examples/marketing-stats/everything.ts index 21f6fa30..733e334a 100644 --- a/examples/marketing-stats/everything.ts +++ b/examples/marketing-stats/everything.ts @@ -30,7 +30,7 @@ async function marketingStatsFlow() { console.log("Campaigns only:", JSON.stringify(campaigns.data, null, 2)); // Narrow further by recipient email service provider; add - // `sending_domain_ids` (from the Sending Domains endpoints) to pick domains. + // `sending_domain_ids` (from `client.domains`) to pick domains. const automationsAtGoogle = await client.marketingStats.get({ ...dateRange, sources: ["automations"], diff --git a/examples/sandbox/attachments.ts b/examples/sandbox/attachments.ts new file mode 100644 index 00000000..259e321b --- /dev/null +++ b/examples/sandbox/attachments.ts @@ -0,0 +1,33 @@ +import { MailtrapClient } from "mailtrap" + +const TOKEN = ""; +const SANDBOX_ID = "" +const ACCOUNT_ID = "" + +const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); + +const sandboxesClient = client.sandbox.sandboxes +const messagesClient = client.sandbox.messages +const attachmentsClient = client.sandbox.attachments + +sandboxesClient.getList() + .then(async (sandboxes) => { + if (sandboxes && sandboxes.length > 0) { + const firstSandboxId = sandboxes[0].id + + const messages = await messagesClient.get(firstSandboxId) + + if (messages && messages.length > 0) { + const firstMessageId = messages[0].id + + const attachments = await attachmentsClient.getList(firstSandboxId, firstMessageId) + + if (attachments && attachments.length > 0) { + const firstAttachment = attachments[0].id + const attachmentData = await attachmentsClient.get(firstSandboxId, firstMessageId, firstAttachment) + + console.log(attachmentData) + } + } + } + }) diff --git a/examples/sandbox/messages.ts b/examples/sandbox/messages.ts new file mode 100644 index 00000000..4fa9aab0 --- /dev/null +++ b/examples/sandbox/messages.ts @@ -0,0 +1,41 @@ +import { MailtrapClient } from "mailtrap" + +const TOKEN = ""; +const SANDBOX_ID = "" +const ACCOUNT_ID = "" + +const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); + +const sandboxesClient = client.sandbox.sandboxes +const messagesClient = client.sandbox.messages + +sandboxesClient.getList() + .then(async (sandboxes) => { + if (sandboxes && sandboxes.length > 0) { + const firstSandboxId = sandboxes[0].id + + const messages = await messagesClient.get(firstSandboxId) + + if (messages && messages.length > 0) { + const firstMessageId = messages[0].id + + await messagesClient.get(firstSandboxId) + await messagesClient.getHtmlAnalysis(firstSandboxId, firstMessageId) + await messagesClient.getHtmlMessage(firstSandboxId, firstMessageId) + await messagesClient.getTextMessage(firstSandboxId, firstMessageId) + await messagesClient.getMailHeaders(firstSandboxId, firstMessageId) + await messagesClient.getMessageAsEml(firstSandboxId, firstMessageId) + await messagesClient.getMessageHtmlSource(firstSandboxId, firstMessageId) + await messagesClient.getRawMessage(firstSandboxId, firstMessageId) + await messagesClient.getSpamScore(firstSandboxId, firstMessageId) + await messagesClient.showEmailMessage(firstSandboxId, firstMessageId) + await messagesClient.updateMessage(firstSandboxId, firstMessageId, { + isRead: false + }) + await messagesClient.forward(firstSandboxId, firstMessageId, 'mock@mail.com') + const response = await messagesClient.deleteMessage(firstSandboxId, firstMessageId) + + console.log(response) + } + } + }) diff --git a/examples/testing/projects.ts b/examples/sandbox/projects.ts similarity index 70% rename from examples/testing/projects.ts rename to examples/sandbox/projects.ts index b7f8520f..74f5025e 100644 --- a/examples/testing/projects.ts +++ b/examples/sandbox/projects.ts @@ -1,12 +1,12 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const TEST_INBOX_ID = "" +const SANDBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); -const projectsClient = client.testing.projects +const projectsClient = client.sandbox.projects projectsClient.getList() .then(async (projects) => { diff --git a/examples/sandbox/sandboxes.ts b/examples/sandbox/sandboxes.ts new file mode 100644 index 00000000..c5677f1a --- /dev/null +++ b/examples/sandbox/sandboxes.ts @@ -0,0 +1,35 @@ +import { MailtrapClient } from "mailtrap" + +const TOKEN = ""; +const SANDBOX_ID = "" +const ACCOUNT_ID = "" + +const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); + +const projectsClient = client.sandbox.projects +const sandboxesClient = client.sandbox.sandboxes + +projectsClient.getList() + .then(async (projects) => { + if (projects && projects.length > 0) { + const firstProjectId = projects[0].id + + await sandboxesClient.create(firstProjectId, 'test-sandbox') + + const sandboxes = await sandboxesClient.getList() + + if (sandboxes && sandboxes.length > 0) { + const firstSandboxId = sandboxes[0].id + + const sandboxAttributes = await sandboxesClient.getSandboxAttributes(firstSandboxId) + await sandboxesClient.updateSandbox(firstSandboxId, {name: 'mock-name', emailUsername: 'mocker'}) + await sandboxesClient.cleanSandbox(firstSandboxId) + await sandboxesClient.markAsRead(firstSandboxId) + await sandboxesClient.resetCredentials(firstSandboxId) + await sandboxesClient.enableEmailAddress(firstSandboxId) + const response = await sandboxesClient.resetEmailAddress(firstSandboxId) + + console.log(response) + } + } + }) diff --git a/examples/testing/send-mail.ts b/examples/sandbox/send-mail.ts similarity index 90% rename from examples/testing/send-mail.ts rename to examples/sandbox/send-mail.ts index 15bbde17..12a784bc 100644 --- a/examples/testing/send-mail.ts +++ b/examples/sandbox/send-mail.ts @@ -9,11 +9,11 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const TEST_INBOX_ID = "" +const SANDBOX_ID = "" const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; -const client = new MailtrapClient({ token: TOKEN, sandbox: true, testInboxId: TEST_INBOX_ID }); +const client = new MailtrapClient({ token: TOKEN, sandbox: true, sandboxId: SANDBOX_ID }); client.send({ from: { name: "Mailtrap Test", email: SENDER_EMAIL }, diff --git a/examples/testing/template.ts b/examples/sandbox/template.ts similarity index 80% rename from examples/testing/template.ts rename to examples/sandbox/template.ts index e39ff57a..46f794bd 100644 --- a/examples/testing/template.ts +++ b/examples/sandbox/template.ts @@ -7,12 +7,13 @@ import { MailtrapClient } from "mailtrap" */ const TOKEN = ""; +const SANDBOX_ID = ""; const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; -const client = new MailtrapClient({ token: TOKEN }); +const client = new MailtrapClient({ token: TOKEN, sandbox: true, sandboxId: SANDBOX_ID }); -client.testing.send({ +client.send({ from: { name: "Mailtrap Test", email: SENDER_EMAIL }, to: [{ email: RECIPIENT_EMAIL }], template_uuid: "813e39db-c74a-4830-b037-0e6ba8b1fe88", diff --git a/examples/testing/transport.ts b/examples/sandbox/transport.ts similarity index 95% rename from examples/testing/transport.ts rename to examples/sandbox/transport.ts index 1a8abf17..dea53105 100644 --- a/examples/testing/transport.ts +++ b/examples/sandbox/transport.ts @@ -9,13 +9,13 @@ import { MailtrapTransport } from "mailtrap" */ const TOKEN = "" -const TEST_INBOX_ID = "" +const SANDBOX_ID = "" const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; const transport = Nodemailer.createTransport(MailtrapTransport({ token: TOKEN, - testInboxId: TEST_INBOX_ID, + sandboxId: SANDBOX_ID, sandbox: true })) diff --git a/examples/sending-domains/everything.ts b/examples/sending-domains/everything.ts deleted file mode 100644 index 4fb7e147..00000000 --- a/examples/sending-domains/everything.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MailtrapClient } from "mailtrap"; - -const TOKEN = ""; -const ACCOUNT_ID = ""; - -const client = new MailtrapClient({ - token: TOKEN, - accountId: Number(ACCOUNT_ID), -}); - -async function sendingDomainsFlow() { - try { - // Get all sending domains - const all = await client.sendingDomains.getList(); - console.log("All sending domains:", JSON.stringify(all, null, 2)); - - if (!all.data || all.data.length === 0) { - console.log("No sending domains found for this account."); - return; - } - - // Get a specific sending domain - const one = await client.sendingDomains.get(all.data[0].id); - console.log("One sending domain:", JSON.stringify(one, null, 2)); - - // Send setup instructions - const setupResponse = await client.sendingDomains.sendSetupInstructions( - all.data[0].id, - "admin@example.com" - ); - console.log("Setup instructions sent"); - - // Create a new sending domain - const created = await client.sendingDomains.create({ - domain_name: "test-domain-" + Date.now() + ".com", - }); - console.log("Created sending domain:", JSON.stringify(created, null, 2)); - - // Update the settings of the created domain - const updated = await client.sendingDomains.update(created.id, { - open_tracking_enabled: true, - click_tracking_enabled: true, - tracking_opt_out_enabled: true, - auto_unsubscribe_link_enabled: false, - inbound_enabled: false, - }); - console.log("Updated sending domain:", JSON.stringify(updated, null, 2)); - - // Delete the created domain - await client.sendingDomains.delete(created.id); - console.log("Sending domain deleted"); - - } catch (error) { - console.error("Error in sendingDomainsFlow:", error instanceof Error ? error.message : String(error)); - } -} - -sendingDomainsFlow(); diff --git a/examples/testing/attachments.ts b/examples/testing/attachments.ts deleted file mode 100644 index 51eaed20..00000000 --- a/examples/testing/attachments.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { MailtrapClient } from "mailtrap" - -const TOKEN = ""; -const TEST_INBOX_ID = "" -const ACCOUNT_ID = "" - -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); - -const inboxesClient = client.testing.inboxes -const messagesClient = client.testing.messages -const attachmentsClient = client.testing.attachments - -inboxesClient.getList() - .then(async (inboxes) => { - if (inboxes && inboxes.length > 0) { - const firstInboxId = inboxes[0].id - - const messages = await messagesClient.get(firstInboxId) - - if (messages && messages.length > 0) { - const firstMessageId = messages[0].id - - const attachments = await attachmentsClient.getList(firstMessageId, firstInboxId) - - if (attachments && attachments.length > 0) { - const firstAttachment = attachments[0].id - const attachmentData = await attachmentsClient.get(firstInboxId, firstMessageId, firstAttachment) - - console.log(attachmentData) - } - } - } - }) diff --git a/examples/testing/inboxes.ts b/examples/testing/inboxes.ts deleted file mode 100644 index fd30a1e1..00000000 --- a/examples/testing/inboxes.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { MailtrapClient } from "mailtrap" - -const TOKEN = ""; -const TEST_INBOX_ID = "" -const ACCOUNT_ID = "" - -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); - -const projectsClient = client.testing.projects -const inboxesClient = client.testing.inboxes - -projectsClient.getList() - .then(async (projects) => { - if (projects && projects.length > 0) { - const firstProjectId = projects[0].id - - await inboxesClient.create(firstProjectId, 'test-inbox') - - const inboxes = await inboxesClient.getList() - - if (inboxes && inboxes.length > 0) { - const firstInboxId = inboxes[0].id - - const inboxAttributes = await inboxesClient.getInboxAttributes(firstInboxId) - await inboxesClient.updateInbox(firstInboxId, {name: 'mock-name', emailUsername: 'mocker'}) - await inboxesClient.cleanInbox(firstInboxId) - await inboxesClient.markAsRead(firstInboxId) - await inboxesClient.resetCredentials(firstInboxId) - await inboxesClient.enableEmailAddress(firstInboxId) - const response = await inboxesClient.resetEmailAddress(firstInboxId) - - console.log(response) - } - } - }) diff --git a/examples/testing/messages.ts b/examples/testing/messages.ts deleted file mode 100644 index 1295a68a..00000000 --- a/examples/testing/messages.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { MailtrapClient } from "mailtrap" - -const TOKEN = ""; -const TEST_INBOX_ID = "" -const ACCOUNT_ID = "" - -const client = new MailtrapClient({ token: TOKEN, testInboxId: TEST_INBOX_ID, accountId: ACCOUNT_ID }); - -const inboxesClient = client.testing.inboxes -const messagesClient = client.testing.messages - -inboxesClient.getList() - .then(async (inboxes) => { - if (inboxes && inboxes.length > 0) { - const firstInboxId = inboxes[0].id - - const messages = await messagesClient.get(firstInboxId) - - if (messages && messages.length > 0) { - const firstMessageId = messages[0].id - - await messagesClient.get(firstInboxId) - await messagesClient.getHtmlAnalysis(firstInboxId, firstMessageId) - await messagesClient.getHtmlMessage(firstInboxId, firstMessageId) - await messagesClient.getTextMessage(firstInboxId, firstMessageId) - await messagesClient.getMailHeaders(firstInboxId, firstMessageId) - await messagesClient.getMessageAsEml(firstInboxId, firstMessageId) - await messagesClient.getMessageHtmlSource(firstInboxId, firstMessageId) - await messagesClient.getRawMessage(firstInboxId, firstMessageId) - await messagesClient.getSpamScore(firstInboxId, firstMessageId) - await messagesClient.showEmailMessage(firstInboxId, firstMessageId) - await messagesClient.updateMessage(firstInboxId, firstMessageId, { - isRead: false - }) - await messagesClient.forward(firstInboxId, firstMessageId, 'mock@mail.com') - const response = await messagesClient.deleteMessage(firstInboxId, firstMessageId) - - console.log(response) - } - } - }) diff --git a/src/__tests__/lib/api/Sandbox.test.ts b/src/__tests__/lib/api/Sandbox.test.ts new file mode 100644 index 00000000..c328a0bf --- /dev/null +++ b/src/__tests__/lib/api/Sandbox.test.ts @@ -0,0 +1,43 @@ +import axios from "axios"; + +import Sandbox from "../../../lib/api/Sandbox"; +import Testing from "../../../lib/api/Testing"; +import InboxesApi from "../../../lib/api/resources/Inboxes"; +import warnDeprecated from "../../../lib/deprecation"; + +jest.mock("../../../lib/deprecation"); + +describe("lib/api/Sandbox: ", () => { + const accountId = 100; + const sandboxAPI = new Sandbox(axios, accountId); + + describe("class Sandbox(): ", () => { + describe("init: ", () => { + it("initializes with all necessary params.", () => { + expect(sandboxAPI).toHaveProperty("projects"); + expect(sandboxAPI).toHaveProperty("sandboxes"); + expect(sandboxAPI).toHaveProperty("messages"); + expect(sandboxAPI).toHaveProperty("attachments"); + }); + }); + }); + + describe("class Testing() (deprecated): ", () => { + const testingAPI = new Testing(axios, accountId); + + it("keeps the Sandbox resources.", () => { + expect(testingAPI).toBeInstanceOf(Sandbox); + }); + + it("inboxes returns the same InboxesApi and warns.", () => { + const { inboxes } = testingAPI; + + expect(inboxes).toBeInstanceOf(InboxesApi); + expect(testingAPI.inboxes).toBe(inboxes); + expect(warnDeprecated).toHaveBeenCalledWith( + "client.testing.inboxes", + "client.sandbox.sandboxes" + ); + }); + }); +}); diff --git a/src/__tests__/lib/api/Testing.test.ts b/src/__tests__/lib/api/Testing.test.ts deleted file mode 100644 index b77b35d3..00000000 --- a/src/__tests__/lib/api/Testing.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import axios from "axios"; - -import Testing from "../../../lib/api/Testing"; - -describe("lib/api/Testing: ", () => { - const testInboxId = 100; - const testingAPI = new Testing(axios, testInboxId); - - describe("class Testing(): ", () => { - describe("init: ", () => { - it("initializes with all necessary params.", () => { - expect(testingAPI).toHaveProperty("projects"); - expect(testingAPI).toHaveProperty("inboxes"); - expect(testingAPI).toHaveProperty("messages"); - expect(testingAPI).toHaveProperty("attachments"); - }); - }); - }); -}); diff --git a/src/__tests__/lib/api/resources/Attachments.test.ts b/src/__tests__/lib/api/resources/Attachments.test.ts index 21a696e8..2c5f5501 100644 --- a/src/__tests__/lib/api/resources/Attachments.test.ts +++ b/src/__tests__/lib/api/resources/Attachments.test.ts @@ -28,7 +28,7 @@ describe("lib/api/resources/Attachments: ", () => { attachment_human_size: "mock-attachment_human_size", download_path: "mock-download_path", }; - const inboxId = 1; + const sandboxId = 1; const messageId = 1; const attachmentId = 100; @@ -58,11 +58,11 @@ describe("lib/api/resources/Attachments: ", () => { describe("getList(): ", () => { it("successfully gets list of attachments.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/attachments`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/attachments`; const expectedResponseData = [responseData]; mock.onGet(endpoint).reply(200, expectedResponseData); - const result = await attachmentsAPI.getList(inboxId, messageId); + const result = await attachmentsAPI.getList(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(expectedResponseData); @@ -74,7 +74,7 @@ describe("lib/api/resources/Attachments: ", () => { expect.assertions(2); try { - await attachmentsAPI.getList(inboxId, messageId); + await attachmentsAPI.getList(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -87,10 +87,14 @@ describe("lib/api/resources/Attachments: ", () => { describe("get(): ", () => { it("successfully gets single attachment.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/attachments/${attachmentId}`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/attachments/${attachmentId}`; mock.onGet(endpoint).reply(200, responseData); - const result = await attachmentsAPI.get(inboxId, messageId, attachmentId); + const result = await attachmentsAPI.get( + sandboxId, + messageId, + attachmentId + ); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(responseData); @@ -102,7 +106,7 @@ describe("lib/api/resources/Attachments: ", () => { expect.assertions(2); try { - await attachmentsAPI.get(inboxId, messageId, attachmentId); + await attachmentsAPI.get(sandboxId, messageId, attachmentId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); diff --git a/src/__tests__/lib/api/resources/SendingDomains.test.ts b/src/__tests__/lib/api/resources/Domains.test.ts similarity index 64% rename from src/__tests__/lib/api/resources/SendingDomains.test.ts rename to src/__tests__/lib/api/resources/Domains.test.ts index ee162484..5cad1923 100644 --- a/src/__tests__/lib/api/resources/SendingDomains.test.ts +++ b/src/__tests__/lib/api/resources/Domains.test.ts @@ -1,14 +1,18 @@ import axios, { AxiosInstance } from "axios"; import MockAdapter from "axios-mock-adapter"; +import DomainsBaseAPI from "../../../../lib/api/Domains"; import SendingDomainsBaseAPI from "../../../../lib/api/SendingDomains"; +import DomainsApi from "../../../../lib/api/resources/Domains"; +import SendingDomainsApi from "../../../../lib/api/resources/SendingDomains"; import { - SendingDomain, + Domain, DnsRecord, - SendingDomainPermissions, -} from "../../../../types/api/sending-domains"; + DomainPermissions, +} from "../../../../types/api/domains"; +import { SendingDomainsResponse } from "../../../../types/api/sending-domains"; -describe("lib/api/SendingDomains: ", () => { +describe("lib/api/Domains: ", () => { const axiosInstance: AxiosInstance = axios.create(); const mock = new MockAdapter(axiosInstance); @@ -16,25 +20,22 @@ describe("lib/api/SendingDomains: ", () => { axiosInstance.interceptors.response.use((response) => response.data); const testAccountId = 100; - const sendingDomainsAPI = new SendingDomainsBaseAPI( - axiosInstance, - testAccountId - ); + const domainsAPI = new DomainsBaseAPI(axiosInstance, testAccountId); - describe("class SendingDomainsBaseAPI(): ", () => { + describe("class DomainsBaseAPI(): ", () => { describe("init: ", () => { it("initializes with all necessary params.", () => { - expect(sendingDomainsAPI).toHaveProperty("get"); - expect(sendingDomainsAPI).toHaveProperty("getList"); - expect(sendingDomainsAPI).toHaveProperty("create"); - expect(sendingDomainsAPI).toHaveProperty("update"); - expect(sendingDomainsAPI).toHaveProperty("delete"); - expect(sendingDomainsAPI).toHaveProperty("sendSetupInstructions"); + expect(domainsAPI).toHaveProperty("get"); + expect(domainsAPI).toHaveProperty("getList"); + expect(domainsAPI).toHaveProperty("create"); + expect(domainsAPI).toHaveProperty("update"); + expect(domainsAPI).toHaveProperty("delete"); + expect(domainsAPI).toHaveProperty("sendSetupInstructions"); }); }); - describe("sendingDomains.getList(): ", () => { - it("should get sending domains list.", async () => { + describe("domains.getList(): ", () => { + it("should get domains list.", async () => { const mockDnsRecords: DnsRecord[] = [ { key: "verification", @@ -54,13 +55,13 @@ describe("lib/api/SendingDomains: ", () => { }, ]; - const mockPermissions: SendingDomainPermissions = { + const mockPermissions: DomainPermissions = { can_read: true, can_update: true, can_destroy: true, }; - const mockSendingDomains: SendingDomain[] = [ + const mockDomains: Domain[] = [ { id: 435, domain_name: "example.com", @@ -87,16 +88,16 @@ describe("lib/api/SendingDomains: ", () => { .onGet( `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains` ) - .reply(200, { data: mockSendingDomains }); + .reply(200, { data: mockDomains }); - const result = await sendingDomainsAPI.getList(); + const result = await domainsAPI.getList(); - expect(result).toEqual({ data: mockSendingDomains }); + expect(result).toEqual({ data: mockDomains }); }); }); - describe("sendingDomains.get(): ", () => { - it("should get a single sending domain by id.", async () => { + describe("domains.get(): ", () => { + it("should get a single domain by id.", async () => { const mockDnsRecords: DnsRecord[] = [ { key: "verification", @@ -108,13 +109,13 @@ describe("lib/api/SendingDomains: ", () => { }, ]; - const mockPermissions: SendingDomainPermissions = { + const mockPermissions: DomainPermissions = { can_read: true, can_update: true, can_destroy: true, }; - const mockSendingDomain: SendingDomain = { + const mockDomain: Domain = { id: 999, domain_name: "example.com", demo: false, @@ -137,18 +138,18 @@ describe("lib/api/SendingDomains: ", () => { mock .onGet( - `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${mockSendingDomain.id}` + `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${mockDomain.id}` ) - .reply(200, mockSendingDomain); + .reply(200, mockDomain); - const result = await sendingDomainsAPI.get(mockSendingDomain.id); + const result = await domainsAPI.get(mockDomain.id); - expect(result).toEqual(mockSendingDomain); + expect(result).toEqual(mockDomain); }); }); - describe("sendingDomains.create(): ", () => { - it("should create a new sending domain.", async () => { + describe("domains.create(): ", () => { + it("should create a new domain.", async () => { const mockDnsRecords: DnsRecord[] = [ { key: "verification", @@ -160,13 +161,13 @@ describe("lib/api/SendingDomains: ", () => { }, ]; - const mockPermissions: SendingDomainPermissions = { + const mockPermissions: DomainPermissions = { can_read: true, can_update: true, can_destroy: true, }; - const mockSendingDomain: SendingDomain = { + const mockDomain: Domain = { id: 436, domain_name: "newdomain.com", demo: false, @@ -196,23 +197,23 @@ describe("lib/api/SendingDomains: ", () => { `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains`, { sending_domain: createParams } ) - .reply(201, mockSendingDomain); + .reply(201, mockDomain); - const result = await sendingDomainsAPI.create(createParams); + const result = await domainsAPI.create(createParams); - expect(result).toEqual(mockSendingDomain); + expect(result).toEqual(mockDomain); }); }); - describe("sendingDomains.update(): ", () => { - it("should update a sending domain.", async () => { - const mockPermissions: SendingDomainPermissions = { + describe("domains.update(): ", () => { + it("should update a domain.", async () => { + const mockPermissions: DomainPermissions = { can_read: true, can_update: true, can_destroy: true, }; - const mockSendingDomain: SendingDomain = { + const mockDomain: Domain = { id: 435, domain_name: "example.com", demo: false, @@ -243,54 +244,73 @@ describe("lib/api/SendingDomains: ", () => { mock .onPatch( - `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${mockSendingDomain.id}`, + `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${mockDomain.id}`, { sending_domain: updateParams } ) - .reply(200, mockSendingDomain); + .reply(200, mockDomain); - const result = await sendingDomainsAPI.update( - mockSendingDomain.id, - updateParams - ); + const result = await domainsAPI.update(mockDomain.id, updateParams); - expect(result).toEqual(mockSendingDomain); + expect(result).toEqual(mockDomain); }); }); - describe("sendingDomains.delete(): ", () => { - it("should delete a sending domain by id.", async () => { - const sendingDomainId = 999; + describe("domains.delete(): ", () => { + it("should delete a domain by id.", async () => { + const domainId = 999; mock .onDelete( - `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${sendingDomainId}` + `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${domainId}` ) .reply(204); - const result = await sendingDomainsAPI.delete(sendingDomainId); + const result = await domainsAPI.delete(domainId); expect(result).toBeUndefined(); }); }); - describe("sendingDomains.sendSetupInstructions(): ", () => { - it("should send setup instructions for a sending domain.", async () => { - const sendingDomainId = 999; + describe("domains.sendSetupInstructions(): ", () => { + it("should send setup instructions for a domain.", async () => { + const domainId = 999; const email = "admin@example.com"; mock .onPost( - `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${sendingDomainId}/send_setup_instructions` + `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${domainId}/send_setup_instructions` ) .reply(204); - const result = await sendingDomainsAPI.sendSetupInstructions( - sendingDomainId, - email - ); + const result = await domainsAPI.sendSetupInstructions(domainId, email); expect(result).toBeUndefined(); }); }); }); + + describe("deprecated SendingDomains names: ", () => { + it("SendingDomainsBaseAPI is a DomainsBaseAPI calling the same endpoints.", async () => { + const sendingDomainsAPI = new SendingDomainsBaseAPI( + axiosInstance, + testAccountId + ); + mock + .onGet( + `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains` + ) + .reply(200, { data: [] }); + + const result: SendingDomainsResponse = await sendingDomainsAPI.getList(); + + expect(sendingDomainsAPI).toBeInstanceOf(DomainsBaseAPI); + expect(result).toEqual({ data: [] }); + }); + + it("SendingDomainsApi is a DomainsApi.", () => { + expect( + new SendingDomainsApi(axiosInstance, testAccountId) + ).toBeInstanceOf(DomainsApi); + }); + }); }); diff --git a/src/__tests__/lib/api/resources/Messages.test.ts b/src/__tests__/lib/api/resources/Messages.test.ts index 9e49e881..85327311 100644 --- a/src/__tests__/lib/api/resources/Messages.test.ts +++ b/src/__tests__/lib/api/resources/Messages.test.ts @@ -14,7 +14,7 @@ describe("lib/api/resources/Messages: ", () => { let mock: AxiosMockAdapter; const accountId = 100; const messagesAPI = new Messages(axios, accountId); - const inboxId = 1; + const sandboxId = 1; const messageId = 1; const responseData = { id: 1, @@ -80,11 +80,11 @@ describe("lib/api/resources/Messages: ", () => { describe("showEmailMessage(): ", () => { it("successfully deletes message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}`; mock.onGet(endpoint).reply(200, responseData); - const result = await messagesAPI.showEmailMessage(inboxId, messageId); + const result = await messagesAPI.showEmailMessage(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(responseData); @@ -96,7 +96,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.showEmailMessage(inboxId, messageId); + await messagesAPI.showEmailMessage(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -109,11 +109,11 @@ describe("lib/api/resources/Messages: ", () => { describe("updateMessage(): ", () => { it("successfully updates message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}`; mock.onPatch(endpoint).reply(200, responseData); - const result = await messagesAPI.updateMessage(inboxId, messageId, { + const result = await messagesAPI.updateMessage(sandboxId, messageId, { isRead: true, }); @@ -129,7 +129,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.updateMessage(inboxId, messageId, { + await messagesAPI.updateMessage(sandboxId, messageId, { isRead: true, }); } catch (error) { @@ -144,11 +144,11 @@ describe("lib/api/resources/Messages: ", () => { describe("deleteMessage(): ", () => { it("successfully deletes message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}`; mock.onDelete(endpoint).reply(200, responseData); - const result = await messagesAPI.deleteMessage(inboxId, messageId); + const result = await messagesAPI.deleteMessage(sandboxId, messageId); expect(mock.history.delete[0].url).toEqual(endpoint); expect(mock.history.delete[0].data).toBeUndefined(); @@ -161,7 +161,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.deleteMessage(inboxId, messageId); + await messagesAPI.deleteMessage(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -174,19 +174,19 @@ describe("lib/api/resources/Messages: ", () => { describe("get(): ", () => { it("successfully gets all messages.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages`; const expectedResponseData = [responseData]; mock.onGet(endpoint).reply(200, expectedResponseData); - const result = await messagesAPI.get(inboxId); + const result = await messagesAPI.get(sandboxId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(expectedResponseData); }); it("successfully gets messages with last_id parameter.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages`; const lastId = 100; const expectedResponseData = [responseData]; @@ -194,7 +194,7 @@ describe("lib/api/resources/Messages: ", () => { .onGet(endpoint, { params: { last_id: lastId } }) .reply(200, expectedResponseData); - const result = await messagesAPI.get(inboxId, { last_id: lastId }); + const result = await messagesAPI.get(sandboxId, { last_id: lastId }); expect(mock.history.get[0].url).toEqual(endpoint); expect(mock.history.get[0].params).toEqual({ last_id: lastId }); @@ -202,7 +202,7 @@ describe("lib/api/resources/Messages: ", () => { }); it("successfully gets messages with page parameter.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages`; const page = 2; const expectedResponseData = [responseData]; @@ -210,7 +210,7 @@ describe("lib/api/resources/Messages: ", () => { .onGet(endpoint, { params: { page } }) .reply(200, expectedResponseData); - const result = await messagesAPI.get(inboxId, { page }); + const result = await messagesAPI.get(sandboxId, { page }); expect(mock.history.get[0].url).toEqual(endpoint); expect(mock.history.get[0].params).toEqual({ page }); @@ -218,7 +218,7 @@ describe("lib/api/resources/Messages: ", () => { }); it("successfully gets messages with search parameter.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages`; const search = "test query"; const expectedResponseData = [responseData]; @@ -226,7 +226,7 @@ describe("lib/api/resources/Messages: ", () => { .onGet(endpoint, { params: { search } }) .reply(200, expectedResponseData); - const result = await messagesAPI.get(inboxId, { search }); + const result = await messagesAPI.get(sandboxId, { search }); expect(mock.history.get[0].url).toEqual(endpoint); expect(mock.history.get[0].params).toEqual({ search }); @@ -234,7 +234,7 @@ describe("lib/api/resources/Messages: ", () => { }); it("successfully gets messages with all query parameters.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages`; const options = { last_id: 100, page: 2, search: "test query" }; const expectedResponseData = [responseData]; @@ -242,7 +242,7 @@ describe("lib/api/resources/Messages: ", () => { .onGet(endpoint, { params: options }) .reply(200, expectedResponseData); - const result = await messagesAPI.get(inboxId, options); + const result = await messagesAPI.get(sandboxId, options); expect(mock.history.get[0].url).toEqual(endpoint); expect(mock.history.get[0].params).toEqual(options); @@ -255,7 +255,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.get(inboxId); + await messagesAPI.get(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -270,12 +270,12 @@ describe("lib/api/resources/Messages: ", () => { const emailToForward = "mock@emailtoforward.com"; it("successfully forwards message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/forward`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/forward`; mock.onPost(endpoint).reply(200, responseData); const result = await messagesAPI.forward( - inboxId, + sandboxId, messageId, emailToForward ); @@ -293,7 +293,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.forward(inboxId, messageId, emailToForward); + await messagesAPI.forward(sandboxId, messageId, emailToForward); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -316,11 +316,11 @@ describe("lib/api/resources/Messages: ", () => { }; it("successfully gets message spam score.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/spam_report`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/spam_report`; mock.onGet(endpoint).reply(200, spamReportData); - const result = await messagesAPI.getSpamScore(inboxId, messageId); + const result = await messagesAPI.getSpamScore(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(spamReportData); @@ -332,7 +332,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getSpamScore(inboxId, messageId); + await messagesAPI.getSpamScore(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -350,11 +350,11 @@ describe("lib/api/resources/Messages: ", () => { }; it("successfully gets html analysis.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/analyze`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/analyze`; mock.onGet(endpoint).reply(200, htmlReportData); - const result = await messagesAPI.getHtmlAnalysis(inboxId, messageId); + const result = await messagesAPI.getHtmlAnalysis(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(htmlReportData); @@ -366,7 +366,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getHtmlAnalysis(inboxId, messageId); + await messagesAPI.getHtmlAnalysis(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -379,11 +379,11 @@ describe("lib/api/resources/Messages: ", () => { describe("getTextMessage(): ", () => { it("successfully gets text message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/body.txt`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/body.txt`; const message = "mock-message"; mock.onGet(endpoint).reply(200, message); - const result = await messagesAPI.getTextMessage(inboxId, messageId); + const result = await messagesAPI.getTextMessage(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(message); @@ -395,7 +395,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getTextMessage(inboxId, messageId); + await messagesAPI.getTextMessage(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -408,11 +408,11 @@ describe("lib/api/resources/Messages: ", () => { describe("getRawMessage(): ", () => { it("successfully gets raw message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/body.raw`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/body.raw`; const message = "mock-message"; mock.onGet(endpoint).reply(200, message); - const result = await messagesAPI.getRawMessage(inboxId, messageId); + const result = await messagesAPI.getRawMessage(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(message); @@ -424,7 +424,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getRawMessage(inboxId, messageId); + await messagesAPI.getRawMessage(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -437,11 +437,14 @@ describe("lib/api/resources/Messages: ", () => { describe("getMessageHtmlSource(): ", () => { it("successfully gets html source.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/body.htmlsource`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/body.htmlsource`; const htmlSource = "mock-htmlSource"; mock.onGet(endpoint).reply(200, htmlSource); - const result = await messagesAPI.getMessageHtmlSource(inboxId, messageId); + const result = await messagesAPI.getMessageHtmlSource( + sandboxId, + messageId + ); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(htmlSource); @@ -453,7 +456,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getMessageHtmlSource(inboxId, messageId); + await messagesAPI.getMessageHtmlSource(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -466,11 +469,11 @@ describe("lib/api/resources/Messages: ", () => { describe("getHtmlMessage(): ", () => { it("successfully gets html message.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/body.html`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/body.html`; const html = "
interesting mock
"; mock.onGet(endpoint).reply(200, html); - const result = await messagesAPI.getHtmlMessage(inboxId, messageId); + const result = await messagesAPI.getHtmlMessage(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(html); @@ -482,7 +485,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getHtmlMessage(inboxId, messageId); + await messagesAPI.getHtmlMessage(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -495,11 +498,11 @@ describe("lib/api/resources/Messages: ", () => { describe("getMessageAsEml(): ", () => { it("successfully gets mail headers.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/body.eml`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/body.eml`; const eml = "mock-eml"; mock.onGet(endpoint).reply(200, eml); - const result = await messagesAPI.getMessageAsEml(inboxId, messageId); + const result = await messagesAPI.getMessageAsEml(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(eml); @@ -511,7 +514,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getMessageAsEml(inboxId, messageId); + await messagesAPI.getMessageAsEml(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -524,11 +527,11 @@ describe("lib/api/resources/Messages: ", () => { describe("getMailHeaders(): ", () => { it("successfully gets message html source.", async () => { - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/messages/${messageId}/mail_headers`; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/messages/${messageId}/mail_headers`; const eml = "mock-eml"; mock.onGet(endpoint).reply(200, eml); - const result = await messagesAPI.getMailHeaders(inboxId, messageId); + const result = await messagesAPI.getMailHeaders(sandboxId, messageId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(eml); @@ -540,7 +543,7 @@ describe("lib/api/resources/Messages: ", () => { expect.assertions(2); try { - await messagesAPI.getMailHeaders(inboxId, messageId); + await messagesAPI.getMailHeaders(sandboxId, messageId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); diff --git a/src/__tests__/lib/api/resources/Inboxes.test.ts b/src/__tests__/lib/api/resources/Sandboxes.test.ts similarity index 61% rename from src/__tests__/lib/api/resources/Inboxes.test.ts rename to src/__tests__/lib/api/resources/Sandboxes.test.ts index cc86e6a9..a2e9df97 100644 --- a/src/__tests__/lib/api/resources/Inboxes.test.ts +++ b/src/__tests__/lib/api/resources/Sandboxes.test.ts @@ -1,19 +1,24 @@ import axios from "axios"; import AxiosMockAdapter from "axios-mock-adapter"; -import Inboxes from "../../../../lib/api/resources/Inboxes"; +import Sandboxes from "../../../../lib/api/resources/Sandboxes"; +import InboxesApi from "../../../../lib/api/resources/Inboxes"; +import warnDeprecated from "../../../../lib/deprecation"; import handleSendingError from "../../../../lib/axios-logger"; import MailtrapError from "../../../../lib/MailtrapError"; import CONFIG from "../../../../config"; +import { Inbox } from "../../../../types/api/inboxes"; + +jest.mock("../../../../lib/deprecation"); const { CLIENT_SETTINGS } = CONFIG; const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; -describe("lib/api/resources/Inboxes: ", () => { +describe("lib/api/resources/Sandboxes: ", () => { let mock: AxiosMockAdapter; const accountId = 100; - const inboxesAPI = new Inboxes(axios, accountId); + const sandboxesAPI = new Sandboxes(axios, accountId); const responseData = { id: 10, name: "mock-name", @@ -40,19 +45,19 @@ describe("lib/api/resources/Inboxes: ", () => { permissions: [], }; - describe("class Inboxes(): ", () => { + describe("class Sandboxes(): ", () => { describe("init: ", () => { it("initializes with all necessary params.", () => { - expect(inboxesAPI).toHaveProperty("cleanInbox"); - expect(inboxesAPI).toHaveProperty("create"); - expect(inboxesAPI).toHaveProperty("delete"); - expect(inboxesAPI).toHaveProperty("enableEmailAddress"); - expect(inboxesAPI).toHaveProperty("getInboxAttributes"); - expect(inboxesAPI).toHaveProperty("getList"); - expect(inboxesAPI).toHaveProperty("markAsRead"); - expect(inboxesAPI).toHaveProperty("resetCredentials"); - expect(inboxesAPI).toHaveProperty("resetEmailAddress"); - expect(inboxesAPI).toHaveProperty("updateInbox"); + expect(sandboxesAPI).toHaveProperty("cleanSandbox"); + expect(sandboxesAPI).toHaveProperty("create"); + expect(sandboxesAPI).toHaveProperty("delete"); + expect(sandboxesAPI).toHaveProperty("enableEmailAddress"); + expect(sandboxesAPI).toHaveProperty("getSandboxAttributes"); + expect(sandboxesAPI).toHaveProperty("getList"); + expect(sandboxesAPI).toHaveProperty("markAsRead"); + expect(sandboxesAPI).toHaveProperty("resetCredentials"); + expect(sandboxesAPI).toHaveProperty("resetEmailAddress"); + expect(sandboxesAPI).toHaveProperty("updateSandbox"); }); }); }); @@ -78,10 +83,10 @@ describe("lib/api/resources/Inboxes: ", () => { const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/projects/${projectId}/inboxes`; mock.onPost(endpoint).reply(200, responseData); - const inboxName = "mock-inboxName"; - const result = await inboxesAPI.create(projectId, inboxName); + const sandboxName = "mock-sandboxName"; + const result = await sandboxesAPI.create(projectId, sandboxName); - const sentData = { inbox: { name: inboxName } }; + const sentData = { inbox: { name: sandboxName } }; expect(mock.history.post[0].url).toEqual(endpoint); expect(mock.history.post[0].data).toEqual(JSON.stringify(sentData)); @@ -90,13 +95,13 @@ describe("lib/api/resources/Inboxes: ", () => { it("fails with error.", async () => { const projectId = 1; - const inboxName = "mock-inboxName"; + const sandboxName = "mock-sandboxName"; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.create(projectId, inboxName); + await sandboxesAPI.create(projectId, sandboxName); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -106,15 +111,15 @@ describe("lib/api/resources/Inboxes: ", () => { } }); - describe("updateInbox(): ", () => { - it("succesfully updates inbox.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}`; + describe("updateSandbox(): ", () => { + it("succesfully updates sandbox.", async () => { + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}`; mock.onPatch(endpoint).reply(200, responseData); - const name = "mock-inboxName"; + const name = "mock-sandboxName"; const emailUsername = "mock@email.com"; - const result = await inboxesAPI.updateInbox(inboxId, { + const result = await sandboxesAPI.updateSandbox(sandboxId, { name, emailUsername, }); @@ -132,16 +137,16 @@ describe("lib/api/resources/Inboxes: ", () => { }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; - const name = "mock-inboxName"; + const name = "mock-sandboxName"; const emailUsername = "mock@email.com"; expect.assertions(2); try { - await inboxesAPI.updateInbox(inboxId, { + await sandboxesAPI.updateSandbox(sandboxId, { name, emailUsername, }); @@ -155,27 +160,27 @@ describe("lib/api/resources/Inboxes: ", () => { }); }); - describe("getInboxAttributes(): ", () => { - it("succesfully gets inbox attributes.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}`; + describe("getSandboxAttributes(): ", () => { + it("succesfully gets sandbox attributes.", async () => { + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}`; mock.onGet(endpoint).reply(200, responseData); - const result = await inboxesAPI.getInboxAttributes(inboxId); + const result = await sandboxesAPI.getSandboxAttributes(sandboxId); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(responseData); }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.getInboxAttributes(inboxId); + await sandboxesAPI.getSandboxAttributes(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -188,26 +193,26 @@ describe("lib/api/resources/Inboxes: ", () => { }); describe("delete(): ", () => { - it("succesfully deletes inbox.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}`; + it("succesfully deletes sandbox.", async () => { + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}`; mock.onDelete(endpoint).reply(200, responseData); - const result = await inboxesAPI.delete(inboxId); + const result = await sandboxesAPI.delete(sandboxId); expect(mock.history.delete[0].url).toEqual(endpoint); expect(result).toEqual(responseData); }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.delete(inboxId); + await sandboxesAPI.delete(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -218,14 +223,14 @@ describe("lib/api/resources/Inboxes: ", () => { }); }); - describe("cleanInbox(): ", () => { - it("succesfully cleans inbox.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/clean`; + describe("cleanSandbox(): ", () => { + it("succesfully cleans sandbox.", async () => { + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/clean`; mock.onPatch(endpoint).reply(200, responseData); - const result = await inboxesAPI.cleanInbox(inboxId); + const result = await sandboxesAPI.cleanSandbox(sandboxId); expect(mock.history.patch[0].url).toEqual(endpoint); expect(mock.history.patch[0].data).toBeUndefined(); @@ -233,13 +238,13 @@ describe("lib/api/resources/Inboxes: ", () => { }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.cleanInbox(inboxId); + await sandboxesAPI.cleanSandbox(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -251,13 +256,13 @@ describe("lib/api/resources/Inboxes: ", () => { }); describe("markAsRead(): ", () => { - it("succesfully marks inbox as read.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/all_read`; + it("succesfully marks sandbox as read.", async () => { + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/all_read`; mock.onPatch(endpoint).reply(200, responseData); - const result = await inboxesAPI.markAsRead(inboxId); + const result = await sandboxesAPI.markAsRead(sandboxId); expect(mock.history.patch[0].url).toEqual(endpoint); expect(mock.history.patch[0].data).toBeUndefined(); @@ -265,13 +270,13 @@ describe("lib/api/resources/Inboxes: ", () => { }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.markAsRead(inboxId); + await sandboxesAPI.markAsRead(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -284,12 +289,12 @@ describe("lib/api/resources/Inboxes: ", () => { describe("resetCredentials(): ", () => { it("succesfully resets credentials.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/reset_credentials`; + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/reset_credentials`; mock.onPatch(endpoint).reply(200, responseData); - const result = await inboxesAPI.resetCredentials(inboxId); + const result = await sandboxesAPI.resetCredentials(sandboxId); expect(mock.history.patch[0].url).toEqual(endpoint); expect(mock.history.patch[0].data).toBeUndefined(); @@ -297,13 +302,13 @@ describe("lib/api/resources/Inboxes: ", () => { }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.resetCredentials(inboxId); + await sandboxesAPI.resetCredentials(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -316,12 +321,12 @@ describe("lib/api/resources/Inboxes: ", () => { describe("enableEmailAddress(): ", () => { it("succesfully toggles email availability.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/toggle_email_username`; + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/toggle_email_username`; mock.onPatch(endpoint).reply(200, responseData); - const result = await inboxesAPI.enableEmailAddress(inboxId); + const result = await sandboxesAPI.enableEmailAddress(sandboxId); expect(mock.history.patch[0].url).toEqual(endpoint); expect(mock.history.patch[0].data).toBeUndefined(); @@ -329,13 +334,13 @@ describe("lib/api/resources/Inboxes: ", () => { }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.enableEmailAddress(inboxId); + await sandboxesAPI.enableEmailAddress(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -348,12 +353,12 @@ describe("lib/api/resources/Inboxes: ", () => { describe("resetEmailAddress(): ", () => { it("succesfully resets email address.", async () => { - const inboxId = 1; - const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${inboxId}/reset_email_username`; + const sandboxId = 1; + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}/reset_email_username`; mock.onPatch(endpoint).reply(200, responseData); - const result = await inboxesAPI.resetEmailAddress(inboxId); + const result = await sandboxesAPI.resetEmailAddress(sandboxId); expect(mock.history.patch[0].url).toEqual(endpoint); expect(mock.history.patch[0].data).toBeUndefined(); @@ -361,13 +366,13 @@ describe("lib/api/resources/Inboxes: ", () => { }); it("fails with error", async () => { - const inboxId = 1; + const sandboxId = 1; const expectedErrorMessage = "Request failed with status code 404"; expect.assertions(2); try { - await inboxesAPI.resetEmailAddress(inboxId); + await sandboxesAPI.resetEmailAddress(sandboxId); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -379,14 +384,14 @@ describe("lib/api/resources/Inboxes: ", () => { }); describe("getList(): ", () => { - it("succesfully gets inbox list.", async () => { + it("succesfully gets sandbox list.", async () => { const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes`; const expectedResponseData = [responseData]; mock.onGet(endpoint).reply(200, expectedResponseData); - const result = await inboxesAPI.getList(); + const result = await sandboxesAPI.getList(); expect(mock.history.get[0].url).toEqual(endpoint); expect(result).toEqual(expectedResponseData); @@ -398,7 +403,7 @@ describe("lib/api/resources/Inboxes: ", () => { expect.assertions(2); try { - await inboxesAPI.getList(); + await sandboxesAPI.getList(); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -408,4 +413,59 @@ describe("lib/api/resources/Inboxes: ", () => { } }); }); + + describe("class InboxesApi() (deprecated): ", () => { + const inboxesAPI = new InboxesApi(axios, accountId); + const sandboxId = 1; + const sandboxURL = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes/${sandboxId}`; + + it("keeps the SandboxesApi methods.", () => { + expect(inboxesAPI).toBeInstanceOf(Sandboxes); + }); + + it("getInboxAttributes() delegates to getSandboxAttributes() and warns.", async () => { + mock.onGet(sandboxURL).reply(200, responseData); + + const result: Inbox = await inboxesAPI.getInboxAttributes(sandboxId); + + expect(mock.history.get[0].url).toEqual(sandboxURL); + expect(result).toEqual(responseData); + expect(warnDeprecated).toHaveBeenCalledWith( + "client.testing.inboxes.getInboxAttributes", + "client.sandbox.sandboxes.getSandboxAttributes" + ); + }); + + it("updateInbox() delegates to updateSandbox() and warns.", async () => { + mock.onPatch(sandboxURL).reply(200, responseData); + + const result = await inboxesAPI.updateInbox(sandboxId, { + name: "mock-sandboxName", + emailUsername: "mock", + }); + + expect(mock.history.patch[0].data).toEqual( + JSON.stringify({ + inbox: { name: "mock-sandboxName", email_username: "mock" }, + }) + ); + expect(result).toEqual(responseData); + expect(warnDeprecated).toHaveBeenCalledWith( + "client.testing.inboxes.updateInbox", + "client.sandbox.sandboxes.updateSandbox" + ); + }); + + it("cleanInbox() delegates to cleanSandbox() and warns.", async () => { + mock.onPatch(`${sandboxURL}/clean`).reply(200, responseData); + + const result = await inboxesAPI.cleanInbox(sandboxId); + + expect(result).toEqual(responseData); + expect(warnDeprecated).toHaveBeenCalledWith( + "client.testing.inboxes.cleanInbox", + "client.sandbox.sandboxes.cleanSandbox" + ); + }); + }); }); diff --git a/src/__tests__/lib/deprecation.test.ts b/src/__tests__/lib/deprecation.test.ts new file mode 100644 index 00000000..c181864f --- /dev/null +++ b/src/__tests__/lib/deprecation.test.ts @@ -0,0 +1,35 @@ +import warnDeprecated from "../../lib/deprecation"; + +describe("lib/deprecation: ", () => { + let emitWarning: jest.SpyInstance; + + beforeEach(() => { + emitWarning = jest + .spyOn(process, "emitWarning") + .mockImplementation(() => {}); + }); + + afterEach(() => { + emitWarning.mockRestore(); + }); + + it("emits a DeprecationWarning naming the replacement.", () => { + warnDeprecated("client.oldName", "client.newName"); + + expect(emitWarning).toHaveBeenCalledWith( + "client.oldName is deprecated and will be removed in a future major release; use client.newName instead.", + { type: "DeprecationWarning", code: "MAILTRAP_DEPRECATED_NAME" } + ); + }); + + it("warns once per old name.", () => { + warnDeprecated("client.firstName", "client.newName"); + warnDeprecated("client.firstName", "client.newName"); + warnDeprecated("client.secondName", "client.newName"); + + expect(emitWarning.mock.calls.map(([message]) => message)).toEqual([ + "client.firstName is deprecated and will be removed in a future major release; use client.newName instead.", + "client.secondName is deprecated and will be removed in a future major release; use client.newName instead.", + ]); + }); +}); diff --git a/src/__tests__/lib/mailtrap-client.test.ts b/src/__tests__/lib/mailtrap-client.test.ts index c972d3f1..a63b728f 100644 --- a/src/__tests__/lib/mailtrap-client.test.ts +++ b/src/__tests__/lib/mailtrap-client.test.ts @@ -8,6 +8,7 @@ import MailtrapError from "../../lib/MailtrapError"; import CONFIG from "../../config"; import GeneralAPI from "../../lib/api/General"; +import SandboxAPI from "../../lib/api/Sandbox"; import TestingAPI from "../../lib/api/Testing"; import ContactLists from "../../lib/api/ContactLists"; import Contacts from "../../lib/api/Contacts"; @@ -15,15 +16,24 @@ import ContactExportsBaseAPI from "../../lib/api/ContactExports"; import TemplatesBaseAPI from "../../lib/api/Templates"; import EmailTemplatesBaseAPI from "../../lib/api/EmailTemplates"; import SuppressionsBaseAPI from "../../lib/api/Suppressions"; +import DomainsBaseAPI from "../../lib/api/Domains"; import SendingDomainsBaseAPI from "../../lib/api/SendingDomains"; import EmailLogsBaseAPI from "../../lib/api/EmailLogs"; import ContactEventsBaseAPI from "../../lib/api/ContactEvents"; import MarketingStatsBaseAPI from "../../lib/api/MarketingStats"; +import warnDeprecated from "../../lib/deprecation"; + +jest.mock("../../lib/deprecation"); const { ERRORS, CLIENT_SETTINGS } = CONFIG; -const { TESTING_ENDPOINT, BULK_ENDPOINT, SENDING_ENDPOINT } = CLIENT_SETTINGS; -const { ACCOUNT_ID_MISSING, BULK_SANDBOX_INCOMPATIBLE, TEST_INBOX_ID_MISSING } = - ERRORS; +const { TESTING_ENDPOINT, BULK_ENDPOINT, SENDING_ENDPOINT, GENERAL_ENDPOINT } = + CLIENT_SETTINGS; +const { + ACCOUNT_ID_MISSING, + BULK_SANDBOX_INCOMPATIBLE, + SANDBOX_ID_CONFLICT, + SANDBOX_ID_MISSING, +} = ERRORS; describe("lib/mailtrap-client: ", () => { let mock: AxiosMockAdapter; @@ -64,15 +74,15 @@ describe("lib/mailtrap-client: ", () => { expect(error).toEqual(new MailtrapError(BULK_SANDBOX_INCOMPATIBLE)); } }); - const testInboxId = 100; + const sandboxId = 100; - it("successfully sends testing email.", async () => { - const testingClient = new MailtrapClient({ + it("successfully sends sandbox email.", async () => { + const sandboxClient = new MailtrapClient({ token: "MY_API_TOKEN", sandbox: true, - testInboxId, + sandboxId, }); - const endpoint = `${TESTING_ENDPOINT}/api/send/${testInboxId}`; + const endpoint = `${TESTING_ENDPOINT}/api/send/${sandboxId}`; const expectedResponseData = { success: true, message_ids: ["0c7fd939-02cf-11ed-88c2-0a58a9feac02"], @@ -95,7 +105,7 @@ describe("lib/mailtrap-client: ", () => { html: "
Mock text
", }; - const result = await testingClient.send(emailData); + const result = await sandboxClient.send(emailData); expect(mock.history.post[0].url).toEqual(endpoint); expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); @@ -138,17 +148,17 @@ describe("lib/mailtrap-client: ", () => { }); it("handles an API error.", async () => { - const testingClient = new MailtrapClient({ + const sandboxClient = new MailtrapClient({ token: "MY_API_TOKEN", sandbox: true, - testInboxId, + sandboxId, }); const responseData = { success: false, errors: ["mock-error-1", "mock-error-2"], }; - const endpoint = `${TESTING_ENDPOINT}/api/send/${testInboxId}`; + const endpoint = `${TESTING_ENDPOINT}/api/send/${sandboxId}`; mock.onPost(endpoint).reply(400, responseData); @@ -173,7 +183,7 @@ describe("lib/mailtrap-client: ", () => { expect.assertions(3); try { - await testingClient.send(emailData); + await sandboxClient.send(emailData); } catch (error) { expect(mock.history.post[0].url).toEqual(endpoint); expect(mock.history.post[0].data).toEqual(JSON.stringify(emailData)); @@ -185,10 +195,10 @@ describe("lib/mailtrap-client: ", () => { }); it("handles an HTTP transport error.", async () => { - const testingClient = new MailtrapClient({ + const sandboxClient = new MailtrapClient({ token: "MY_API_TOKEN", sandbox: true, - testInboxId, + sandboxId, }); const emailData = { from: { @@ -211,7 +221,7 @@ describe("lib/mailtrap-client: ", () => { expect.assertions(2); try { - await testingClient.send(emailData); + await sandboxClient.send(emailData); } catch (error) { expect(error).toBeInstanceOf(MailtrapError); @@ -338,7 +348,7 @@ describe("lib/mailtrap-client: ", () => { } }); - it("throws MailtrapError(TEST_INBOX_ID_MISSING) when sending in sandbox mode without testInboxId", async () => { + it("throws MailtrapError(SANDBOX_ID_MISSING) when sending in sandbox mode without sandboxId", async () => { const client = new MailtrapClient({ token: "MY_API_TOKEN", sandbox: true, @@ -352,11 +362,11 @@ describe("lib/mailtrap-client: ", () => { subject: "Test", text: "Body", }) - ).rejects.toEqual(new MailtrapError(TEST_INBOX_ID_MISSING)); + ).rejects.toEqual(new MailtrapError(SANDBOX_ID_MISSING)); }); describe("batch sending:", () => { - it("throws MailtrapError(TEST_INBOX_ID_MISSING) when batch sending in sandbox mode without testInboxId", async () => { + it("throws MailtrapError(SANDBOX_ID_MISSING) when batch sending in sandbox mode without sandboxId", async () => { const client = new MailtrapClient({ token: "MY_API_TOKEN", sandbox: true, @@ -377,7 +387,7 @@ describe("lib/mailtrap-client: ", () => { }; await expect(client.batchSend(batchData)).rejects.toEqual( - new MailtrapError(TEST_INBOX_ID_MISSING) + new MailtrapError(SANDBOX_ID_MISSING) ); }); @@ -487,7 +497,7 @@ describe("lib/mailtrap-client: ", () => { const batchClient = new MailtrapClient({ token: "MY_API_TOKEN", sandbox: true, - testInboxId: 100, + sandboxId: 100, }); const endpoint = `${TESTING_ENDPOINT}/api/batch/100`; const expectedBatchResponseData = { @@ -712,7 +722,7 @@ describe("lib/mailtrap-client: ", () => { }); }); - describe("get testing(): ", () => { + describe("get sandbox(): ", () => { it("rejects with Mailtrap error, when `accountId` is missing.", () => { const client = new MailtrapClient({ token: "MY_API_TOKEN", @@ -721,33 +731,22 @@ describe("lib/mailtrap-client: ", () => { expect.assertions(1); try { - client.testing; + client.sandbox; } catch (error) { expect(error).toEqual(new MailtrapError(ACCOUNT_ID_MISSING)); } }); - it("returns testing API object when accountId is provided, even without testInboxId", () => { + it("returns sandbox API object when accountId is provided, even without sandboxId", () => { const client = new MailtrapClient({ token: "MY_API_TOKEN", accountId: 123, - // testInboxId is intentionally omitted - }); - expect.assertions(1); - - const testingClient = client.testing; - expect(testingClient).toBeInstanceOf(TestingAPI); - }); - - it("returns testing API object, console warn is called twice.", () => { - const client = new MailtrapClient({ - token: "MY_API_TOKEN", - accountId: 10, + // sandboxId is intentionally omitted }); expect.assertions(1); - const testingClient = client.testing; - expect(testingClient).toBeInstanceOf(TestingAPI); + const sandboxClient = client.sandbox; + expect(sandboxClient).toBeInstanceOf(SandboxAPI); }); describe("get general(): ", () => { @@ -932,7 +931,7 @@ describe("lib/mailtrap-client: ", () => { }); }); - describe("get sendingDomains(): ", () => { + describe("get domains(): ", () => { it("rejects with Mailtrap error, when `accountId` is missing.", () => { expect.assertions(1); @@ -940,12 +939,12 @@ describe("lib/mailtrap-client: ", () => { token: "test-token", }); - expect(() => client.sendingDomains).toThrow( + expect(() => client.domains).toThrow( "accountId is missing, please provide a valid accountId." ); }); - it("returns sending domains API object when accountId is provided.", () => { + it("returns domains API object when accountId is provided.", () => { expect.assertions(1); const client = new MailtrapClient({ @@ -953,8 +952,8 @@ describe("lib/mailtrap-client: ", () => { accountId: 123, }); - const sendingDomainsClient = client.sendingDomains; - expect(sendingDomainsClient).toBeInstanceOf(SendingDomainsBaseAPI); + const domainsClient = client.domains; + expect(domainsClient).toBeInstanceOf(DomainsBaseAPI); }); }); @@ -1022,4 +1021,113 @@ describe("lib/mailtrap-client: ", () => { }); }); }); + + describe("deprecated names: ", () => { + const accountId = 123; + + beforeAll(() => { + mock = new AxiosMockAdapter(axios); + }); + + afterEach(() => { + mock.reset(); + }); + + describe("testInboxId config option: ", () => { + it("still sets the sandbox to send to and warns.", async () => { + const endpoint = `${TESTING_ENDPOINT}/api/send/100`; + mock.onPost(endpoint).reply(200, { success: true, message_ids: [] }); + + await new MailtrapClient({ + token: "MY_API_TOKEN", + sandbox: true, + testInboxId: 100, + }).send(goodMail); + + expect(mock.history.post[0].url).toEqual(endpoint); + expect(warnDeprecated).toHaveBeenCalledWith("testInboxId", "sandboxId"); + }); + + it("accepts the same value as sandboxId.", () => { + expect( + () => + new MailtrapClient({ + token: "MY_API_TOKEN", + sandboxId: 100, + testInboxId: 100, + }) + ).not.toThrow(); + }); + + it("throws MailtrapError(SANDBOX_ID_CONFLICT) when it differs from sandboxId.", () => { + expect( + () => + new MailtrapClient({ + token: "MY_API_TOKEN", + sandboxId: 100, + testInboxId: 200, + }) + ).toThrow(new MailtrapError(SANDBOX_ID_CONFLICT)); + }); + + it("does not warn when only sandboxId is set.", () => { + // eslint-disable-next-line no-new + new MailtrapClient({ token: "MY_API_TOKEN", sandboxId: 100 }); + + expect(warnDeprecated).not.toHaveBeenCalled(); + }); + }); + + describe("get testing(): ", () => { + it("rejects with Mailtrap error, when `accountId` is missing.", () => { + const client = new MailtrapClient({ token: "MY_API_TOKEN" }); + + expect(() => client.testing).toThrow( + new MailtrapError(ACCOUNT_ID_MISSING) + ); + }); + + it("client.testing.inboxes.getList() still resolves and warns.", async () => { + const client = new MailtrapClient({ token: "MY_API_TOKEN", accountId }); + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes`; + mock.onGet(endpoint).reply(200, [{ id: 1 }]); + + const result = await client.testing.inboxes.getList(); + + expect(client.testing).toBeInstanceOf(TestingAPI); + expect(result).toEqual([{ id: 1 }]); + expect(mock.history.get[0].url).toEqual(endpoint); + expect(warnDeprecated).toHaveBeenCalledWith( + "client.testing", + "client.sandbox" + ); + }); + }); + + describe("get sendingDomains(): ", () => { + it("rejects with Mailtrap error, when `accountId` is missing.", () => { + const client = new MailtrapClient({ token: "MY_API_TOKEN" }); + + expect(() => client.sendingDomains).toThrow( + new MailtrapError(ACCOUNT_ID_MISSING) + ); + }); + + it("client.sendingDomains.getList() still resolves and warns.", async () => { + const client = new MailtrapClient({ token: "MY_API_TOKEN", accountId }); + const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/sending_domains`; + mock.onGet(endpoint).reply(200, { data: [] }); + + const result = await client.sendingDomains.getList(); + + expect(client.sendingDomains).toBeInstanceOf(SendingDomainsBaseAPI); + expect(result).toEqual({ data: [] }); + expect(mock.history.get[0].url).toEqual(endpoint); + expect(warnDeprecated).toHaveBeenCalledWith( + "client.sendingDomains", + "client.domains" + ); + }); + }); + }); }); diff --git a/src/config/index.ts b/src/config/index.ts index 6c94d3d5..f593c5ed 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -6,7 +6,9 @@ export default { FROM_REQUIRED: "From is required.", SENDING_FAILED: "Sending failed.", NO_DATA_ERROR: "No Data.", - TEST_INBOX_ID_MISSING: "testInboxId is missing, testing API will not work.", + SANDBOX_ID_MISSING: "sandboxId is missing, sandbox sending will not work.", + SANDBOX_ID_CONFLICT: + "sandboxId and testInboxId are set to different values; set only sandboxId.", ACCOUNT_ID_MISSING: "accountId is missing, please provide a valid accountId.", ORGANIZATION_ID_MISSING: diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index 9638a5a9..921b10a1 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -19,6 +19,7 @@ import EmailLogsBaseAPI from "./api/EmailLogs"; import GeneralAPI from "./api/General"; import InboundAPI from "./api/Inbound"; import MarketingStatsBaseAPI from "./api/MarketingStats"; +import DomainsBaseAPI from "./api/Domains"; import SendingDomainsBaseAPI from "./api/SendingDomains"; import StatsBaseAPI from "./api/Stats"; import SuppressionsBaseAPI from "./api/Suppressions"; @@ -26,9 +27,12 @@ import TrackingOptOutsBaseAPI from "./api/TrackingOptOuts"; import OrganizationsBaseAPI from "./api/Organizations"; import TemplatesBaseAPI from "./api/Templates"; import EmailTemplatesBaseAPI from "./api/EmailTemplates"; +import SandboxAPI from "./api/Sandbox"; import TestingAPI from "./api/Testing"; import WebhooksBaseAPI from "./api/Webhooks"; +import warnDeprecated from "./deprecation"; + import CONFIG from "../config"; import { @@ -52,7 +56,8 @@ const { ACCOUNT_ID_MISSING, BULK_SANDBOX_INCOMPATIBLE, ORGANIZATION_ID_MISSING, - TEST_INBOX_ID_MISSING, + SANDBOX_ID_CONFLICT, + SANDBOX_ID_MISSING, } = ERRORS; /** @@ -61,7 +66,7 @@ const { export default class MailtrapClient { private axios: AxiosInstance; - private testInboxId?: number; + private sandboxId?: number; private accountId?: number; @@ -69,13 +74,14 @@ export default class MailtrapClient { private bulk: boolean; - private sandbox: boolean; + private sandboxMode: boolean; /** * Initalizes axios instance with Mailtrap params. */ constructor({ token, + sandboxId, testInboxId, accountId, organizationId, @@ -83,6 +89,14 @@ export default class MailtrapClient { sandbox = false, userAgent, }: MailtrapClientConfig) { + if (testInboxId !== undefined) { + if (sandboxId !== undefined && sandboxId !== testInboxId) { + throw new MailtrapError(SANDBOX_ID_CONFLICT); + } + + warnDeprecated("testInboxId", "sandboxId"); + } + this.axios = axios.create({ httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), @@ -101,11 +115,11 @@ export default class MailtrapClient { handleSendingError ); - this.testInboxId = testInboxId; + this.sandboxId = sandboxId ?? testInboxId; this.accountId = accountId; this.organizationId = organizationId; this.bulk = bulk; - this.sandbox = sandbox; + this.sandboxMode = sandbox; } /** @@ -129,18 +143,27 @@ export default class MailtrapClient { } /** - * Validates that test inbox ID is present, throws MailtrapError if missing. + * Validates that sandbox ID is present, throws MailtrapError if missing. */ - private validateTestInboxIdPresence(): void { - if (this.sandbox && !this.testInboxId) { - throw new MailtrapError(TEST_INBOX_ID_MISSING); + private validateSandboxIdPresence(): void { + if (this.sandboxMode && !this.sandboxId) { + throw new MailtrapError(SANDBOX_ID_MISSING); } } /** - * Getter for Testing API. Warns if some of the required keys are missing. + * Getter for Email Sandbox API: projects, sandboxes, messages and attachments. + */ + get sandbox() { + const accountId = this.validateAccountIdPresence(); + return new SandboxAPI(this.axios, accountId); + } + + /** + * @deprecated Use `client.sandbox` instead. */ get testing() { + warnDeprecated("client.testing", "client.sandbox"); const accountId = this.validateAccountIdPresence(); return new TestingAPI(this.axios, accountId); } @@ -244,9 +267,18 @@ export default class MailtrapClient { } /** - * Getter for Sending Domains API. + * Getter for Domains API. + */ + get domains() { + const accountId = this.validateAccountIdPresence(); + return new DomainsBaseAPI(this.axios, accountId); + } + + /** + * @deprecated Use `client.domains` instead. */ get sendingDomains() { + warnDeprecated("client.sendingDomains", "client.domains"); const accountId = this.validateAccountIdPresence(); return new SendingDomainsBaseAPI(this.axios, accountId); } @@ -311,9 +343,9 @@ export default class MailtrapClient { private determineHost() { let host; - if (this.bulk && this.sandbox) { + if (this.bulk && this.sandboxMode) { throw new MailtrapError(BULK_SANDBOX_INCOMPATIBLE); - } else if (this.sandbox) { + } else if (this.sandboxMode) { host = TESTING_ENDPOINT; } else if (this.bulk) { host = BULK_ENDPOINT; @@ -329,10 +361,10 @@ export default class MailtrapClient { */ public async send(mail: Mail): Promise { const host = this.determineHost(); - this.validateTestInboxIdPresence(); + this.validateSandboxIdPresence(); const url = `${host}/api/send${ - this.sandbox && this.testInboxId ? `/${this.testInboxId}` : "" + this.sandboxMode && this.sandboxId ? `/${this.sandboxId}` : "" }`; const preparedMail = encodeMailBuffers(mail); @@ -350,10 +382,10 @@ export default class MailtrapClient { const { requests, base } = request; const host = this.determineHost(); - this.validateTestInboxIdPresence(); + this.validateSandboxIdPresence(); const sandbox = - this.sandbox && this.testInboxId ? `/${this.testInboxId}` : ""; + this.sandboxMode && this.sandboxId ? `/${this.sandboxId}` : ""; const url = `${host}/api/batch${sandbox}`; diff --git a/src/lib/api/Domains.ts b/src/lib/api/Domains.ts new file mode 100644 index 00000000..ae9e84fd --- /dev/null +++ b/src/lib/api/Domains.ts @@ -0,0 +1,27 @@ +import { AxiosInstance } from "axios"; + +import DomainsApi from "./resources/Domains"; + +export default class DomainsBaseAPI { + public get: DomainsApi["get"]; + + public getList: DomainsApi["getList"]; + + public create: DomainsApi["create"]; + + public update: DomainsApi["update"]; + + public delete: DomainsApi["delete"]; + + public sendSetupInstructions: DomainsApi["sendSetupInstructions"]; + + constructor(client: AxiosInstance, accountId: number) { + const domains = new DomainsApi(client, accountId); + this.get = domains.get.bind(domains); + this.getList = domains.getList.bind(domains); + this.create = domains.create.bind(domains); + this.update = domains.update.bind(domains); + this.delete = domains.delete.bind(domains); + this.sendSetupInstructions = domains.sendSetupInstructions.bind(domains); + } +} diff --git a/src/lib/api/Sandbox.ts b/src/lib/api/Sandbox.ts new file mode 100644 index 00000000..064e0ace --- /dev/null +++ b/src/lib/api/Sandbox.ts @@ -0,0 +1,23 @@ +import { AxiosInstance } from "axios"; + +import ProjectsApi from "./resources/Projects"; +import SandboxesApi from "./resources/Sandboxes"; +import MessagesApi from "./resources/Messages"; +import AttachmentsApi from "./resources/Attachments"; + +export default class SandboxAPI { + public projects: ProjectsApi; + + public sandboxes: SandboxesApi; + + public messages: MessagesApi; + + public attachments: AttachmentsApi; + + constructor(client: AxiosInstance, accountId: number) { + this.projects = new ProjectsApi(client, accountId); + this.sandboxes = new SandboxesApi(client, accountId); + this.messages = new MessagesApi(client, accountId); + this.attachments = new AttachmentsApi(client, accountId); + } +} diff --git a/src/lib/api/SendingDomains.ts b/src/lib/api/SendingDomains.ts index 0810bca3..fa1a3a87 100644 --- a/src/lib/api/SendingDomains.ts +++ b/src/lib/api/SendingDomains.ts @@ -1,31 +1,4 @@ -import { AxiosInstance } from "axios"; +import DomainsBaseAPI from "./Domains"; -import SendingDomainsApi from "./resources/SendingDomains"; - -export default class SendingDomainsBaseAPI { - private client: AxiosInstance; - - public get: SendingDomainsApi["get"]; - - public getList: SendingDomainsApi["getList"]; - - public create: SendingDomainsApi["create"]; - - public update: SendingDomainsApi["update"]; - - public delete: SendingDomainsApi["delete"]; - - public sendSetupInstructions: SendingDomainsApi["sendSetupInstructions"]; - - constructor(client: AxiosInstance, accountId: number) { - this.client = client; - const sendingDomains = new SendingDomainsApi(this.client, accountId); - this.get = sendingDomains.get.bind(sendingDomains); - this.getList = sendingDomains.getList.bind(sendingDomains); - this.create = sendingDomains.create.bind(sendingDomains); - this.update = sendingDomains.update.bind(sendingDomains); - this.delete = sendingDomains.delete.bind(sendingDomains); - this.sendSetupInstructions = - sendingDomains.sendSetupInstructions.bind(sendingDomains); - } -} +/** @deprecated Use `DomainsBaseAPI` (`client.domains`) instead. */ +export default class SendingDomainsBaseAPI extends DomainsBaseAPI {} diff --git a/src/lib/api/Testing.ts b/src/lib/api/Testing.ts index 7569fd24..e31afba9 100644 --- a/src/lib/api/Testing.ts +++ b/src/lib/api/Testing.ts @@ -1,23 +1,22 @@ import { AxiosInstance } from "axios"; -import ProjectsApi from "./resources/Projects"; +import SandboxAPI from "./Sandbox"; import InboxesApi from "./resources/Inboxes"; -import MessagesApi from "./resources/Messages"; -import AttachmentsApi from "./resources/Attachments"; +import warnDeprecated from "../deprecation"; -export default class TestingAPI { - public projects: ProjectsApi; +/** @deprecated Use `SandboxAPI` (`client.sandbox`) instead. */ +export default class TestingAPI extends SandboxAPI { + private inboxesApi: InboxesApi; - public inboxes: InboxesApi; - - public messages: MessagesApi; + constructor(client: AxiosInstance, accountId: number) { + super(client, accountId); + this.inboxesApi = new InboxesApi(client, accountId); + } - public attachments: AttachmentsApi; + /** @deprecated Use `client.sandbox.sandboxes` instead. */ + get inboxes() { + warnDeprecated("client.testing.inboxes", "client.sandbox.sandboxes"); - constructor(client: AxiosInstance, accountId: number) { - this.projects = new ProjectsApi(client, accountId); - this.inboxes = new InboxesApi(client, accountId); - this.messages = new MessagesApi(client, accountId); - this.attachments = new AttachmentsApi(client, accountId); + return this.inboxesApi; } } diff --git a/src/lib/api/resources/Attachments.ts b/src/lib/api/resources/Attachments.ts index f6622bb4..7a102d62 100644 --- a/src/lib/api/resources/Attachments.ts +++ b/src/lib/api/resources/Attachments.ts @@ -10,27 +10,27 @@ const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; export default class AttachmentsApi { private client: AxiosInstance; - private inboxesURL: string; + private sandboxesURL: string; constructor(client: AxiosInstance, accountId: number) { this.client = client; - this.inboxesURL = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes`; + this.sandboxesURL = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/inboxes`; } /** - * Get message attachments by `inboxId` and `messageId`. + * Get message attachments by `sandboxId` and `messageId`. */ - public async getList(inboxId: number, messageId: number) { - const url = `${this.inboxesURL}/${inboxId}/messages/${messageId}/attachments`; + public async getList(sandboxId: number, messageId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/messages/${messageId}/attachments`; return this.client.get(url); } /** - * Get message attachments by `inboxId`.`messageId` and `attachmentId`. + * Get message attachments by `sandboxId`.`messageId` and `attachmentId`. */ - public async get(inboxId: number, messageId: number, attachmentId: number) { - const url = `${this.inboxesURL}/${inboxId}/messages/${messageId}/attachments/${attachmentId}`; + public async get(sandboxId: number, messageId: number, attachmentId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/messages/${messageId}/attachments/${attachmentId}`; return this.client.get(url); } diff --git a/src/lib/api/resources/CompanyInfo.ts b/src/lib/api/resources/CompanyInfo.ts index 03468a0b..b7f6d795 100644 --- a/src/lib/api/resources/CompanyInfo.ts +++ b/src/lib/api/resources/CompanyInfo.ts @@ -21,9 +21,9 @@ export default class CompanyInfoApi { } /** - * Get the company info associated with a sending domain. - * @param domainId Sending domain ID - * @returns Returns the company info of the sending domain + * Get the company info associated with a domain. + * @param domainId Domain ID + * @returns Returns the company info of the domain */ public async get(domainId: number) { const url = this.companyInfoURL(domainId); @@ -32,9 +32,9 @@ export default class CompanyInfoApi { } /** - * Create the company info for a sending domain. Company info is required for + * Create the company info for a domain. Company info is required for * domain compliance verification. - * @param domainId Sending domain ID + * @param domainId Domain ID * @param params Company info attributes * @returns Returns the created company info */ @@ -49,9 +49,9 @@ export default class CompanyInfoApi { } /** - * Update the company info for a sending domain. Only the fields provided are + * Update the company info for a domain. Only the fields provided are * updated. - * @param domainId Sending domain ID + * @param domainId Domain ID * @param params Company info attributes to update * @returns Returns the updated company info */ diff --git a/src/lib/api/resources/Domains.ts b/src/lib/api/resources/Domains.ts new file mode 100644 index 00000000..b0447c80 --- /dev/null +++ b/src/lib/api/resources/Domains.ts @@ -0,0 +1,89 @@ +import { AxiosInstance } from "axios"; + +import CONFIG from "../../../config"; +import { + CreateDomainParams, + Domain, + DomainsResponse, + UpdateDomainParams, +} from "../../../types/api/domains"; + +const { CLIENT_SETTINGS } = CONFIG; +const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; + +export default class DomainsApi { + private client: AxiosInstance; + + private domainsURL: string; + + constructor(client: AxiosInstance, accountId: number) { + this.client = client; + this.domainsURL = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/sending_domains`; + } + + /** + * Get a list of domains. + * @returns Returns the list of domains for the account. + */ + public async getList() { + const url = this.domainsURL; + + return this.client.get(url); + } + + /** + * Get a single domain by ID. + * @param id Domain ID + * @returns Returns a single domain + */ + public async get(id: number) { + const url = `${this.domainsURL}/${id}`; + + return this.client.get(url); + } + + /** + * Create a new domain. + */ + public async create(params: CreateDomainParams) { + const url = this.domainsURL; + const data = { sending_domain: params }; + + return this.client.post(url, data); + } + + /** + * Update configuration settings for a domain. + * @param id Domain ID + * @param params Settings to update + * @returns Returns the updated domain + */ + public async update(id: number, params: UpdateDomainParams) { + const url = `${this.domainsURL}/${id}`; + const data = { sending_domain: params }; + + return this.client.patch(url, data); + } + + /** + * Delete a domain by ID. + * @param id Domain ID + */ + public async delete(id: number) { + const url = `${this.domainsURL}/${id}`; + + return this.client.delete(url); + } + + /** + * Send setup instructions for a domain to an email address. + * @param id Domain ID + * @param email Email address to send setup instructions to + * @returns Returns a success message + */ + public async sendSetupInstructions(id: number, email: string) { + const url = `${this.domainsURL}/${id}/send_setup_instructions`; + + return this.client.post(url, { email }); + } +} diff --git a/src/lib/api/resources/EmailCampaigns.ts b/src/lib/api/resources/EmailCampaigns.ts index d2d1b7a1..ed2bcb58 100644 --- a/src/lib/api/resources/EmailCampaigns.ts +++ b/src/lib/api/resources/EmailCampaigns.ts @@ -51,7 +51,7 @@ export default class EmailCampaignsApi { /** * Create a new email campaign in the `draft` state. The campaign must - * reference an existing sending domain via `domain_id` and include + * reference an existing domain via `domain_id` and include * a template `subject` within `template_attributes`. */ public async create(params: CreateEmailCampaignParams) { diff --git a/src/lib/api/resources/Inboxes.ts b/src/lib/api/resources/Inboxes.ts index 4c53fda4..63f5886a 100644 --- a/src/lib/api/resources/Inboxes.ts +++ b/src/lib/api/resources/Inboxes.ts @@ -1,117 +1,37 @@ -import { AxiosInstance } from "axios"; +import SandboxesApi from "./Sandboxes"; +import warnDeprecated from "../../deprecation"; -import CONFIG from "../../../config"; +import { UpdateSandboxParams } from "../../../types/api/sandboxes"; -import { Inbox, UpdateInboxParams } from "../../../types/api/inboxes"; - -const { CLIENT_SETTINGS } = CONFIG; -const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; - -export default class InboxesApi { - private client: AxiosInstance; - - private accountId: number; - - private inboxesURL: string; - - constructor(client: AxiosInstance, accountId: number) { - this.client = client; - this.accountId = accountId; - this.inboxesURL = `${GENERAL_ENDPOINT}/api/accounts/${this.accountId}/inboxes`; - } - - /** - * Creates an inbox in a project. - */ - public async create(projectId: number, inboxName: string) { - const url = `${GENERAL_ENDPOINT}/api/accounts/${this.accountId}/projects/${projectId}/inboxes`; - const data = { inbox: { name: inboxName } }; - - return this.client.post(url, data); - } - - /** - * Gets inbox attributes by inbox id. - */ +/** @deprecated Use `SandboxesApi` (`client.sandbox.sandboxes`) instead. */ +export default class InboxesApi extends SandboxesApi { + /** @deprecated Use `getSandboxAttributes` instead. */ public async getInboxAttributes(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}`; + warnDeprecated( + "client.testing.inboxes.getInboxAttributes", + "client.sandbox.sandboxes.getSandboxAttributes" + ); - return this.client.get(url); + return this.getSandboxAttributes(inboxId); } - /** - * Deletes an inbox with all its emails. - */ - public async delete(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}`; + /** @deprecated Use `updateSandbox` instead. */ + public async updateInbox(inboxId: number, params: UpdateSandboxParams) { + warnDeprecated( + "client.testing.inboxes.updateInbox", + "client.sandbox.sandboxes.updateSandbox" + ); - return this.client.delete(url); + return this.updateSandbox(inboxId, params); } - /** - * Updates inbox name, inbox email username. - */ - public async updateInbox(inboxId: number, params: UpdateInboxParams) { - const url = `${this.inboxesURL}/${inboxId}`; - const data = { - inbox: { - name: params.name, - email_username: params.emailUsername, - }, - }; - - return this.client.patch(url, data); - } - - /** - * Deletes all messages (emails) from inbox. - */ + /** @deprecated Use `cleanSandbox` instead. */ public async cleanInbox(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}/clean`; - - return this.client.patch(url); - } - - /** - * Marks all messages in the inbox as read. - */ - public async markAsRead(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}/all_read`; - - return this.client.patch(url); - } - - /** - * Resets SMTP credentials of the inbox. - */ - public async resetCredentials(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}/reset_credentials`; - - return this.client.patch(url); - } - - /** - * Turns the email address of the inbox on/off. - */ - public async enableEmailAddress(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}/toggle_email_username`; - - return this.client.patch(url); - } - - /** - * Resets username of email address per inbox. - */ - public async resetEmailAddress(inboxId: number) { - const url = `${this.inboxesURL}/${inboxId}/reset_email_username`; - - return this.client.patch(url); - } + warnDeprecated( + "client.testing.inboxes.cleanInbox", + "client.sandbox.sandboxes.cleanSandbox" + ); - /** - * Gets a list of inboxes. - */ - public async getList() { - return this.client.get(this.inboxesURL); + return this.cleanSandbox(inboxId); } } diff --git a/src/lib/api/resources/Messages.ts b/src/lib/api/resources/Messages.ts index cb8186eb..d87f0df1 100644 --- a/src/lib/api/resources/Messages.ts +++ b/src/lib/api/resources/Messages.ts @@ -27,8 +27,8 @@ export default class MessagesApi { /** * Gets email message by given `messageId`. */ - public async showEmailMessage(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}`; + public async showEmailMessage(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}`; return this.client.get(url); } @@ -37,30 +37,30 @@ export default class MessagesApi { * Updates message attributes (right now only the is_read attribute is available for modification). */ public async updateMessage( - inboxId: number, + sandboxId: number, messageId: number, params: MessageUpdateParams ) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}`; + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}`; const data = { message: { is_read: params.isRead } }; return this.client.patch(url, data); } /** - * Deletes message from inbox. + * Deletes message from sandbox. */ - public async deleteMessage(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}`; + public async deleteMessage(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}`; return this.client.delete(url); } /** - * Gets all messages in inboxes. + * Gets all messages in a sandbox. */ - public async get(inboxId: number, options?: MessageListOptions) { - const url = `${this.messagesURL}/${inboxId}/messages`; + public async get(sandboxId: number, options?: MessageListOptions) { + const url = `${this.messagesURL}/${sandboxId}/messages`; const params = { ...(options?.last_id !== undefined && { last_id: options.last_id }), @@ -75,11 +75,11 @@ export default class MessagesApi { * Forwards message to an email address. The email address must be confirmed by the recipient in advance. */ public async forward( - inboxId: number, + sandboxId: number, messageId: number, emailToForward: string ) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/forward`; + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/forward`; const data = { email: emailToForward }; return this.client.post(url, data); @@ -88,8 +88,8 @@ export default class MessagesApi { /** * Gets a brief spam report by given `messageId`. */ - public async getSpamScore(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/spam_report`; + public async getSpamScore(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/spam_report`; return this.client.get(url); } @@ -97,8 +97,8 @@ export default class MessagesApi { /** * Gets a brief HTML report by message ID. */ - public async getHtmlAnalysis(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/analyze`; + public async getHtmlAnalysis(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/analyze`; return this.client.get(url); } @@ -106,8 +106,8 @@ export default class MessagesApi { /** * Gets text email body, if it exists. */ - public async getTextMessage(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/body.txt`; + public async getTextMessage(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/body.txt`; return this.client.get(url); } @@ -115,8 +115,8 @@ export default class MessagesApi { /** * Gets raw email body. */ - public async getRawMessage(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/body.raw`; + public async getRawMessage(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/body.raw`; return this.client.get(url); } @@ -124,8 +124,8 @@ export default class MessagesApi { /** * Gets HTML source of email. */ - public async getMessageHtmlSource(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/body.htmlsource`; + public async getMessageHtmlSource(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/body.htmlsource`; return this.client.get(url); } @@ -133,8 +133,8 @@ export default class MessagesApi { /** * Gets formatted HTML email body. Not applicable for plain text emails. */ - public async getHtmlMessage(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/body.html`; + public async getHtmlMessage(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/body.html`; return this.client.get(url); } @@ -142,8 +142,8 @@ export default class MessagesApi { /** * Gets email message in .eml format. */ - public async getMessageAsEml(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/body.eml`; + public async getMessageAsEml(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/body.eml`; return this.client.get(url); } @@ -151,8 +151,8 @@ export default class MessagesApi { /** * Gets mail headers of a message. */ - public async getMailHeaders(inboxId: number, messageId: number) { - const url = `${this.messagesURL}/${inboxId}/messages/${messageId}/mail_headers`; + public async getMailHeaders(sandboxId: number, messageId: number) { + const url = `${this.messagesURL}/${sandboxId}/messages/${messageId}/mail_headers`; return this.client.get(url); } diff --git a/src/lib/api/resources/Permissions.ts b/src/lib/api/resources/Permissions.ts index 4829da93..87377700 100644 --- a/src/lib/api/resources/Permissions.ts +++ b/src/lib/api/resources/Permissions.ts @@ -47,7 +47,7 @@ export default class PermissionsApi { } /** - * Get all resources in your account (Inboxes, Projects, Domains, Billing and Account itself) to which the token has admin access. + * Get all resources in your account (Sandboxes, Projects, Domains, Billing and Account itself) to which the token has admin access. * @returns Returns the resources nested according to their hierarchy. */ public async getResources() { diff --git a/src/lib/api/resources/Projects.ts b/src/lib/api/resources/Projects.ts index e34952ee..c5aad927 100644 --- a/src/lib/api/resources/Projects.ts +++ b/src/lib/api/resources/Projects.ts @@ -27,14 +27,14 @@ export default class ProjectsApi { } /** - * Lists projects and their inboxes to which the API token has access. + * Lists projects and their sandboxes to which the API token has access. */ public async getList() { return this.client.get(this.projectsURL); } /** - * Gets the project and it's inboxes. + * Gets the project and its sandboxes. */ public async getById(projectId: number) { const url = `${this.projectsURL}/${projectId}`; diff --git a/src/lib/api/resources/Sandboxes.ts b/src/lib/api/resources/Sandboxes.ts new file mode 100644 index 00000000..3c888dc4 --- /dev/null +++ b/src/lib/api/resources/Sandboxes.ts @@ -0,0 +1,117 @@ +import { AxiosInstance } from "axios"; + +import CONFIG from "../../../config"; + +import { Sandbox, UpdateSandboxParams } from "../../../types/api/sandboxes"; + +const { CLIENT_SETTINGS } = CONFIG; +const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; + +export default class SandboxesApi { + private client: AxiosInstance; + + private accountId: number; + + private sandboxesURL: string; + + constructor(client: AxiosInstance, accountId: number) { + this.client = client; + this.accountId = accountId; + this.sandboxesURL = `${GENERAL_ENDPOINT}/api/accounts/${this.accountId}/inboxes`; + } + + /** + * Creates a sandbox in a project. + */ + public async create(projectId: number, sandboxName: string) { + const url = `${GENERAL_ENDPOINT}/api/accounts/${this.accountId}/projects/${projectId}/inboxes`; + const data = { inbox: { name: sandboxName } }; + + return this.client.post(url, data); + } + + /** + * Gets sandbox attributes by sandbox id. + */ + public async getSandboxAttributes(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}`; + + return this.client.get(url); + } + + /** + * Deletes a sandbox with all its emails. + */ + public async delete(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}`; + + return this.client.delete(url); + } + + /** + * Updates sandbox name, sandbox email username. + */ + public async updateSandbox(sandboxId: number, params: UpdateSandboxParams) { + const url = `${this.sandboxesURL}/${sandboxId}`; + const data = { + inbox: { + name: params.name, + email_username: params.emailUsername, + }, + }; + + return this.client.patch(url, data); + } + + /** + * Deletes all messages (emails) from sandbox. + */ + public async cleanSandbox(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/clean`; + + return this.client.patch(url); + } + + /** + * Marks all messages in the sandbox as read. + */ + public async markAsRead(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/all_read`; + + return this.client.patch(url); + } + + /** + * Resets SMTP credentials of the sandbox. + */ + public async resetCredentials(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/reset_credentials`; + + return this.client.patch(url); + } + + /** + * Turns the email address of the sandbox on/off. + */ + public async enableEmailAddress(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/toggle_email_username`; + + return this.client.patch(url); + } + + /** + * Resets username of email address per sandbox. + */ + public async resetEmailAddress(sandboxId: number) { + const url = `${this.sandboxesURL}/${sandboxId}/reset_email_username`; + + return this.client.patch(url); + } + + /** + * Gets a list of sandboxes. + */ + public async getList() { + return this.client.get(this.sandboxesURL); + } +} diff --git a/src/lib/api/resources/SendingDomains.ts b/src/lib/api/resources/SendingDomains.ts index 5c39b6e2..0a05b6ed 100644 --- a/src/lib/api/resources/SendingDomains.ts +++ b/src/lib/api/resources/SendingDomains.ts @@ -1,89 +1,4 @@ -import { AxiosInstance } from "axios"; +import DomainsApi from "./Domains"; -import CONFIG from "../../../config"; -import { - CreateSendingDomainParams, - SendingDomain, - SendingDomainsResponse, - UpdateSendingDomainParams, -} from "../../../types/api/sending-domains"; - -const { CLIENT_SETTINGS } = CONFIG; -const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; - -export default class SendingDomainsApi { - private client: AxiosInstance; - - private sendingDomainsURL: string; - - constructor(client: AxiosInstance, accountId: number) { - this.client = client; - this.sendingDomainsURL = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/sending_domains`; - } - - /** - * Get a list of sending domains. - * @returns Returns the list of sending domains for the account. - */ - public async getList() { - const url = this.sendingDomainsURL; - - return this.client.get(url); - } - - /** - * Get a single sending domain by ID. - * @param id Sending domain ID - * @returns Returns a single sending domain - */ - public async get(id: number) { - const url = `${this.sendingDomainsURL}/${id}`; - - return this.client.get(url); - } - - /** - * Create a new sending domain. - */ - public async create(params: CreateSendingDomainParams) { - const url = this.sendingDomainsURL; - const data = { sending_domain: params }; - - return this.client.post(url, data); - } - - /** - * Update configuration settings for a sending domain. - * @param id Sending domain ID - * @param params Settings to update - * @returns Returns the updated sending domain - */ - public async update(id: number, params: UpdateSendingDomainParams) { - const url = `${this.sendingDomainsURL}/${id}`; - const data = { sending_domain: params }; - - return this.client.patch(url, data); - } - - /** - * Delete a sending domain by ID. - * @param id Sending domain ID - */ - public async delete(id: number) { - const url = `${this.sendingDomainsURL}/${id}`; - - return this.client.delete(url); - } - - /** - * Send setup instructions for a sending domain to an email address. - * @param id Sending domain ID - * @param email Email address to send setup instructions to - * @returns Returns a success message - */ - public async sendSetupInstructions(id: number, email: string) { - const url = `${this.sendingDomainsURL}/${id}/send_setup_instructions`; - - return this.client.post(url, { email }); - } -} +/** @deprecated Use `DomainsApi` instead. */ +export default class SendingDomainsApi extends DomainsApi {} diff --git a/src/lib/api/resources/TrackingOptOuts.ts b/src/lib/api/resources/TrackingOptOuts.ts index 93721b60..83be0e1d 100644 --- a/src/lib/api/resources/TrackingOptOuts.ts +++ b/src/lib/api/resources/TrackingOptOuts.ts @@ -42,7 +42,7 @@ export default class TrackingOptOutsApi { } /** - * Add an email address to the tracking opt-out list for a sending domain. + * Add an email address to the tracking opt-out list for a domain. */ public async create(params: CreateTrackingOptOutParams) { return this.client.post< diff --git a/src/lib/deprecation.ts b/src/lib/deprecation.ts new file mode 100644 index 00000000..e02c08d5 --- /dev/null +++ b/src/lib/deprecation.ts @@ -0,0 +1,15 @@ +const warned = new Set(); + +/** + * Emits a `DeprecationWarning` for a renamed API, once per process per old name, + * so a deprecated call inside a loop warns only the first time. + */ +export default function warnDeprecated(oldName: string, newName: string) { + if (warned.has(oldName)) return; + + warned.add(oldName); + process.emitWarning( + `${oldName} is deprecated and will be removed in a future major release; use ${newName} instead.`, + { type: "DeprecationWarning", code: "MAILTRAP_DEPRECATED_NAME" } + ); +} diff --git a/src/types/api/domains.ts b/src/types/api/domains.ts new file mode 100644 index 00000000..3b4b6368 --- /dev/null +++ b/src/types/api/domains.ts @@ -0,0 +1,51 @@ +export interface DnsRecord { + key: string; + domain: string; + type: string; + value: string; + status: string; + name: string; +} + +export interface DomainPermissions { + can_read: boolean; + can_update: boolean; + can_destroy: boolean; +} + +export interface Domain { + id: number; + domain_name: string; + demo: boolean; + compliance_status: string; + dns_verified: boolean; + dns_verified_at: string | null; + dns_records: DnsRecord[]; + open_tracking_enabled: boolean; + click_tracking_enabled: boolean; + tracking_opt_out_enabled: boolean; + auto_unsubscribe_link_enabled: boolean; + custom_domain_tracking_enabled: boolean; + health_alerts_enabled: boolean; + critical_alerts_enabled: boolean; + alert_recipient_email: string | null; + inbound_enabled: boolean; + inbound_verified: boolean; + permissions: DomainPermissions; +} + +export interface CreateDomainParams { + domain_name: string; +} + +export interface DomainsResponse { + data: Domain[]; +} + +export interface UpdateDomainParams { + open_tracking_enabled?: boolean; + click_tracking_enabled?: boolean; + tracking_opt_out_enabled?: boolean; + auto_unsubscribe_link_enabled?: boolean; + inbound_enabled?: boolean; +} diff --git a/src/types/api/email-campaigns.ts b/src/types/api/email-campaigns.ts index e62671e0..52b6ed08 100644 --- a/src/types/api/email-campaigns.ts +++ b/src/types/api/email-campaigns.ts @@ -91,7 +91,7 @@ export type CampaignTemplate = { export type EmailCampaign = { id: number; - /** ID of the sending domain, as returned by the Sending Domains endpoints. */ + /** ID of the domain, as returned by `client.domains`. */ domain_id: number; domain_name: string; name: string; @@ -125,7 +125,7 @@ export type ListEmailCampaignsParams = { export type CreateEmailCampaignParams = { name: string; - /** ID of the verified sending domain, as returned by the Sending Domains endpoints. */ + /** ID of the verified domain, as returned by `client.domains`. */ domain_id: number; from_local_part: string; from_display_name?: string; @@ -139,7 +139,7 @@ export type CreateEmailCampaignParams = { export type UpdateEmailCampaignParams = { name?: string; - /** ID of the verified sending domain, as returned by the Sending Domains endpoints. */ + /** ID of the verified domain, as returned by `client.domains`. */ domain_id?: number; from_local_part?: string; from_display_name?: string; diff --git a/src/types/api/inboxes.ts b/src/types/api/inboxes.ts index f6a3378c..dc22168f 100644 --- a/src/types/api/inboxes.ts +++ b/src/types/api/inboxes.ts @@ -1,37 +1,7 @@ -export type UpdateInboxParams = { - name: string; - emailUsername: string; -}; +import { Sandbox, UpdateSandboxParams } from "./sandboxes"; -type Permissions = { - can_read: boolean; - can_update: boolean; - can_destroy: boolean; - can_leave: boolean; -}; +/** @deprecated Use `Sandbox` instead. */ +export type Inbox = Sandbox; -export type Inbox = { - id: number; - name: string; - username: string; - password: string; - max_size: number; - status: string; - email_username: string; - email_username_enabled: boolean; - sent_messages_count: number; - forwarded_messages_count: number; - used: boolean; - forward_from_email_address: string; - project_id: number; - domain: string; - pop3_domain: string; - email_domain: string; - smtp_ports: number[]; - pop3_ports: number[]; - emails_count: number; - emails_unread_count: number; - last_message_sent_at: string | null; - max_message_size: number; - permissions: Permissions; -}; +/** @deprecated Use `UpdateSandboxParams` instead. */ +export type UpdateInboxParams = UpdateSandboxParams; diff --git a/src/types/api/marketing-stats.ts b/src/types/api/marketing-stats.ts index e509da80..3cd09ed5 100644 --- a/src/types/api/marketing-stats.ts +++ b/src/types/api/marketing-stats.ts @@ -28,7 +28,7 @@ export type MarketingStatsFilterParams = { start_date: string; /** End of the period (inclusive), `YYYY-MM-DD`. */ end_date: string; - /** IDs of the sending domains to include, as returned by the Sending Domains endpoints. */ + /** IDs of the domains to include, as returned by `client.domains`. */ sending_domain_ids?: number[]; /** One source narrows the results to it; omit, or pass both, for all Email Marketing traffic. */ sources?: MarketingStatsSource[]; diff --git a/src/types/api/projects.ts b/src/types/api/projects.ts index 9a637714..151e9f5d 100644 --- a/src/types/api/projects.ts +++ b/src/types/api/projects.ts @@ -1,4 +1,4 @@ -import { Inbox } from "./inboxes"; +import { Sandbox } from "./sandboxes"; type ShareLinks = { admin: string; @@ -16,6 +16,6 @@ export type Project = { id: number; name: string; share_links: ShareLinks; - inboxes: Inbox[]; + inboxes: Sandbox[]; permissions: Permissions; }; diff --git a/src/types/api/sandboxes.ts b/src/types/api/sandboxes.ts new file mode 100644 index 00000000..3f18ea84 --- /dev/null +++ b/src/types/api/sandboxes.ts @@ -0,0 +1,37 @@ +export type UpdateSandboxParams = { + name: string; + emailUsername: string; +}; + +type Permissions = { + can_read: boolean; + can_update: boolean; + can_destroy: boolean; + can_leave: boolean; +}; + +export type Sandbox = { + id: number; + name: string; + username: string; + password: string; + max_size: number; + status: string; + email_username: string; + email_username_enabled: boolean; + sent_messages_count: number; + forwarded_messages_count: number; + used: boolean; + forward_from_email_address: string; + project_id: number; + domain: string; + pop3_domain: string; + email_domain: string; + smtp_ports: number[]; + pop3_ports: number[]; + emails_count: number; + emails_unread_count: number; + last_message_sent_at: string | null; + max_message_size: number; + permissions: Permissions; +}; diff --git a/src/types/api/sending-domains.ts b/src/types/api/sending-domains.ts index 5a9947fe..1cdd38b1 100644 --- a/src/types/api/sending-domains.ts +++ b/src/types/api/sending-domains.ts @@ -1,51 +1,24 @@ -export interface DnsRecord { - key: string; - domain: string; - type: string; - value: string; - status: string; - name: string; -} +import { + CreateDomainParams, + Domain, + DomainPermissions, + DomainsResponse, + UpdateDomainParams, +} from "./domains"; -export interface SendingDomainPermissions { - can_read: boolean; - can_update: boolean; - can_destroy: boolean; -} +export type { DnsRecord } from "./domains"; -export interface SendingDomain { - id: number; - domain_name: string; - demo: boolean; - compliance_status: string; - dns_verified: boolean; - dns_verified_at: string | null; - dns_records: DnsRecord[]; - open_tracking_enabled: boolean; - click_tracking_enabled: boolean; - tracking_opt_out_enabled: boolean; - auto_unsubscribe_link_enabled: boolean; - custom_domain_tracking_enabled: boolean; - health_alerts_enabled: boolean; - critical_alerts_enabled: boolean; - alert_recipient_email: string | null; - inbound_enabled: boolean; - inbound_verified: boolean; - permissions: SendingDomainPermissions; -} +/** @deprecated Use `DomainPermissions` instead. */ +export type SendingDomainPermissions = DomainPermissions; -export interface CreateSendingDomainParams { - domain_name: string; -} +/** @deprecated Use `Domain` instead. */ +export type SendingDomain = Domain; -export interface SendingDomainsResponse { - data: SendingDomain[]; -} +/** @deprecated Use `CreateDomainParams` instead. */ +export type CreateSendingDomainParams = CreateDomainParams; -export interface UpdateSendingDomainParams { - open_tracking_enabled?: boolean; - click_tracking_enabled?: boolean; - tracking_opt_out_enabled?: boolean; - auto_unsubscribe_link_enabled?: boolean; - inbound_enabled?: boolean; -} +/** @deprecated Use `DomainsResponse` instead. */ +export type SendingDomainsResponse = DomainsResponse; + +/** @deprecated Use `UpdateDomainParams` instead. */ +export type UpdateSendingDomainParams = UpdateDomainParams; diff --git a/src/types/mailtrap.ts b/src/types/mailtrap.ts index ecce670a..d0fa7ed2 100644 --- a/src/types/mailtrap.ts +++ b/src/types/mailtrap.ts @@ -70,6 +70,9 @@ export type SendError = { export type MailtrapClientConfig = { token: string; + /** ID of the sandbox that `sandbox: true` sends to. */ + sandboxId?: number; + /** @deprecated Use `sandboxId` instead. */ testInboxId?: number; accountId?: number; organizationId?: number; From c769b14c5a8a8c8ddbf6bba7fde721a1b6418ec8 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 3 Nov 2026 12:02:43 +0100 Subject: [PATCH 2/4] Treat null IDs as unset and fix the sandbox and domains examples - A null sandboxId or testInboxId now counts as not set. The fallback already worked this way, but the conflict check and the deprecation warning didn't. - The sandboxes example works on the sandbox it creates instead of the account's first one. It cleans that sandbox and resets its credentials, so it shouldn't touch an existing one. - The domains example still creates a domain when the account has none. - The sandbox examples that don't send email no longer pass sandboxId, which only applies to sending. Co-Authored-By: Claude Opus 5.5 --- examples/domains/everything.ts | 25 ++++++++++--------- examples/sandbox/attachments.ts | 3 +-- examples/sandbox/messages.ts | 4 +--- examples/sandbox/projects.ts | 3 +-- examples/sandbox/sandboxes.ts | 29 ++++++++++------------- src/__tests__/lib/mailtrap-client.test.ts | 23 ++++++++++++++++++ src/lib/MailtrapClient.ts | 5 ++-- 7 files changed, 54 insertions(+), 38 deletions(-) diff --git a/examples/domains/everything.ts b/examples/domains/everything.ts index 1426318b..f6a821e1 100644 --- a/examples/domains/everything.ts +++ b/examples/domains/everything.ts @@ -14,22 +14,21 @@ async function domainsFlow() { const all = await client.domains.getList(); console.log("All domains:", JSON.stringify(all, null, 2)); - if (!all.data || all.data.length === 0) { + if (all.data && all.data.length > 0) { + // Get a specific domain + const one = await client.domains.get(all.data[0].id); + console.log("One domain:", JSON.stringify(one, null, 2)); + + // Send setup instructions + await client.domains.sendSetupInstructions( + all.data[0].id, + "admin@example.com" + ); + console.log("Setup instructions sent"); + } else { console.log("No domains found for this account."); - return; } - // Get a specific domain - const one = await client.domains.get(all.data[0].id); - console.log("One domain:", JSON.stringify(one, null, 2)); - - // Send setup instructions - const setupResponse = await client.domains.sendSetupInstructions( - all.data[0].id, - "admin@example.com" - ); - console.log("Setup instructions sent"); - // Create a new domain const created = await client.domains.create({ domain_name: "test-domain-" + Date.now() + ".com", diff --git a/examples/sandbox/attachments.ts b/examples/sandbox/attachments.ts index 259e321b..639c8b07 100644 --- a/examples/sandbox/attachments.ts +++ b/examples/sandbox/attachments.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const SANDBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const sandboxesClient = client.sandbox.sandboxes const messagesClient = client.sandbox.messages diff --git a/examples/sandbox/messages.ts b/examples/sandbox/messages.ts index 4fa9aab0..c10c46f2 100644 --- a/examples/sandbox/messages.ts +++ b/examples/sandbox/messages.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const SANDBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const sandboxesClient = client.sandbox.sandboxes const messagesClient = client.sandbox.messages @@ -19,7 +18,6 @@ sandboxesClient.getList() if (messages && messages.length > 0) { const firstMessageId = messages[0].id - await messagesClient.get(firstSandboxId) await messagesClient.getHtmlAnalysis(firstSandboxId, firstMessageId) await messagesClient.getHtmlMessage(firstSandboxId, firstMessageId) await messagesClient.getTextMessage(firstSandboxId, firstMessageId) diff --git a/examples/sandbox/projects.ts b/examples/sandbox/projects.ts index 74f5025e..a71d9d73 100644 --- a/examples/sandbox/projects.ts +++ b/examples/sandbox/projects.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const SANDBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const projectsClient = client.sandbox.projects diff --git a/examples/sandbox/sandboxes.ts b/examples/sandbox/sandboxes.ts index c5677f1a..d86872a8 100644 --- a/examples/sandbox/sandboxes.ts +++ b/examples/sandbox/sandboxes.ts @@ -1,10 +1,9 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; -const SANDBOX_ID = "" const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, sandboxId: SANDBOX_ID, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); const projectsClient = client.sandbox.projects const sandboxesClient = client.sandbox.sandboxes @@ -14,22 +13,20 @@ projectsClient.getList() if (projects && projects.length > 0) { const firstProjectId = projects[0].id - await sandboxesClient.create(firstProjectId, 'test-sandbox') + // Work on the sandbox created here, so the account's existing sandboxes stay untouched. + const { id: sandboxId } = await sandboxesClient.create(firstProjectId, 'test-sandbox') const sandboxes = await sandboxesClient.getList() + console.log(sandboxes) - if (sandboxes && sandboxes.length > 0) { - const firstSandboxId = sandboxes[0].id - - const sandboxAttributes = await sandboxesClient.getSandboxAttributes(firstSandboxId) - await sandboxesClient.updateSandbox(firstSandboxId, {name: 'mock-name', emailUsername: 'mocker'}) - await sandboxesClient.cleanSandbox(firstSandboxId) - await sandboxesClient.markAsRead(firstSandboxId) - await sandboxesClient.resetCredentials(firstSandboxId) - await sandboxesClient.enableEmailAddress(firstSandboxId) - const response = await sandboxesClient.resetEmailAddress(firstSandboxId) - - console.log(response) - } + const sandboxAttributes = await sandboxesClient.getSandboxAttributes(sandboxId) + await sandboxesClient.updateSandbox(sandboxId, {name: 'mock-name', emailUsername: 'mocker'}) + await sandboxesClient.cleanSandbox(sandboxId) + await sandboxesClient.markAsRead(sandboxId) + await sandboxesClient.resetCredentials(sandboxId) + await sandboxesClient.enableEmailAddress(sandboxId) + const response = await sandboxesClient.resetEmailAddress(sandboxId) + + console.log(response) } }) diff --git a/src/__tests__/lib/mailtrap-client.test.ts b/src/__tests__/lib/mailtrap-client.test.ts index a63b728f..9c3fd43e 100644 --- a/src/__tests__/lib/mailtrap-client.test.ts +++ b/src/__tests__/lib/mailtrap-client.test.ts @@ -1070,6 +1070,29 @@ describe("lib/mailtrap-client: ", () => { ).toThrow(new MailtrapError(SANDBOX_ID_CONFLICT)); }); + it("treats null as not set, like undefined.", async () => { + const endpoint = `${TESTING_ENDPOINT}/api/send/100`; + mock.onPost(endpoint).reply(200, { success: true, message_ids: [] }); + + await new MailtrapClient({ + token: "MY_API_TOKEN", + sandbox: true, + sandboxId: 100, + testInboxId: null as unknown as number, + }).send(goodMail); + + expect(mock.history.post[0].url).toEqual(endpoint); + expect(warnDeprecated).not.toHaveBeenCalled(); + expect( + () => + new MailtrapClient({ + token: "MY_API_TOKEN", + sandboxId: null as unknown as number, + testInboxId: 100, + }) + ).not.toThrow(); + }); + it("does not warn when only sandboxId is set.", () => { // eslint-disable-next-line no-new new MailtrapClient({ token: "MY_API_TOKEN", sandboxId: 100 }); diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index 921b10a1..59a2c024 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -89,8 +89,9 @@ export default class MailtrapClient { sandbox = false, userAgent, }: MailtrapClientConfig) { - if (testInboxId !== undefined) { - if (sandboxId !== undefined && sandboxId !== testInboxId) { + // `!= null` matches the `??` fallback below: `null` means "not set" too. + if (testInboxId != null) { + if (sandboxId != null && sandboxId !== testInboxId) { throw new MailtrapError(SANDBOX_ID_CONFLICT); } From a3d9199733a8534bcb8e7cf494435dbc5164e3e6 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 3 Nov 2026 12:42:24 +0100 Subject: [PATCH 3/4] Pass a numeric accountId in the sandbox examples Decisions: - accountId is typed number, so passing the placeholder string made these examples fail type-checking against the SDK; convert it with Number() as the domains example already does Co-Authored-By: Claude Opus 5.5 --- examples/sandbox/attachments.ts | 2 +- examples/sandbox/messages.ts | 2 +- examples/sandbox/projects.ts | 2 +- examples/sandbox/sandboxes.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/sandbox/attachments.ts b/examples/sandbox/attachments.ts index 639c8b07..0c0e054d 100644 --- a/examples/sandbox/attachments.ts +++ b/examples/sandbox/attachments.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const sandboxesClient = client.sandbox.sandboxes const messagesClient = client.sandbox.messages diff --git a/examples/sandbox/messages.ts b/examples/sandbox/messages.ts index c10c46f2..780f6e9d 100644 --- a/examples/sandbox/messages.ts +++ b/examples/sandbox/messages.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const sandboxesClient = client.sandbox.sandboxes const messagesClient = client.sandbox.messages diff --git a/examples/sandbox/projects.ts b/examples/sandbox/projects.ts index a71d9d73..3ea61805 100644 --- a/examples/sandbox/projects.ts +++ b/examples/sandbox/projects.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const projectsClient = client.sandbox.projects diff --git a/examples/sandbox/sandboxes.ts b/examples/sandbox/sandboxes.ts index d86872a8..db164e40 100644 --- a/examples/sandbox/sandboxes.ts +++ b/examples/sandbox/sandboxes.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const projectsClient = client.sandbox.projects const sandboxesClient = client.sandbox.sandboxes From 959cea06dac5c1db7785cef65c54663485ac63ad Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Wed, 4 Nov 2026 14:14:05 +0100 Subject: [PATCH 4/4] Drop unimportable types from the README and pass numeric sandbox IDs The SDK exports no API types from its entry point, so the README's type-rename rows pointed at names users can't import. The sandbox, batch and general examples passed string placeholders to the numeric sandboxId and accountId options, which doesn't compile in TypeScript. Co-Authored-By: Claude Opus 5.5 --- README.md | 4 +--- examples/batch/sandbox.ts | 2 +- examples/general/account-accesses.ts | 2 +- examples/general/accounts.ts | 2 +- examples/general/billing.ts | 2 +- examples/general/permissions.ts | 2 +- examples/sandbox/send-mail.ts | 2 +- examples/sandbox/template.ts | 2 +- examples/sandbox/transport.ts | 2 +- 9 files changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 05e1a3ed..a3bf34ec 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ Versions of this package up to 2.0.2 were an [unofficial client](https://github. ### Renamed APIs -The SDK now uses the product's names: Email Sandbox inboxes are **sandboxes** and sending domains are **domains**. The old names still work, call the same endpoints, and will be removed in a future major release. Each old option, getter and method emits a `DeprecationWarning` the first time it is used, and the old types are marked `@deprecated`. +The SDK now uses the product's names: Email Sandbox inboxes are **sandboxes** and sending domains are **domains**. The old names still work, call the same endpoints, and will be removed in a future major release. Each old option, getter and method emits a `DeprecationWarning` the first time it is used. | Old name | New name | |---|---| @@ -317,8 +317,6 @@ The SDK now uses the product's names: Email Sandbox inboxes are **sandboxes** an | `inboxes.updateInbox()` | `sandboxes.updateSandbox()` | | `inboxes.cleanInbox()` | `sandboxes.cleanSandbox()` | | `client.sendingDomains` | `client.domains` | -| `Inbox`, `UpdateInboxParams` types | `Sandbox`, `UpdateSandboxParams` | -| `SendingDomain`, `SendingDomainPermissions`, `CreateSendingDomainParams`, `UpdateSendingDomainParams`, `SendingDomainsResponse` types | `Domain`, `DomainPermissions`, `CreateDomainParams`, `UpdateDomainParams`, `DomainsResponse` | The other methods of `client.testing` and `client.sendingDomains` keep their names. Fields that mirror the API, such as `inbox_id` on messages or `sending_domain_id` on email logs, keep their names too. Passing both `sandboxId` and `testInboxId` with different values throws a `MailtrapError`. diff --git a/examples/batch/sandbox.ts b/examples/batch/sandbox.ts index 1af24a0f..920ce412 100644 --- a/examples/batch/sandbox.ts +++ b/examples/batch/sandbox.ts @@ -14,7 +14,7 @@ const RECIPIENT_EMAIL = ""; const client = new MailtrapClient({ token: TOKEN, sandbox: true, - sandboxId: SANDBOX_ID + sandboxId: Number(SANDBOX_ID) }); client.batchSend({ diff --git a/examples/general/account-accesses.ts b/examples/general/account-accesses.ts index 1aa68545..fe87bec8 100644 --- a/examples/general/account-accesses.ts +++ b/examples/general/account-accesses.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const accountAccessesClient = client.general.accountAccesses diff --git a/examples/general/accounts.ts b/examples/general/accounts.ts index 973678c6..f1abbae2 100644 --- a/examples/general/accounts.ts +++ b/examples/general/accounts.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const accountsClient = client.general.accounts diff --git a/examples/general/billing.ts b/examples/general/billing.ts index dee555e7..c09c7393 100644 --- a/examples/general/billing.ts +++ b/examples/general/billing.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const billingClient = client.general.billing diff --git a/examples/general/permissions.ts b/examples/general/permissions.ts index 72f0081e..fc552737 100644 --- a/examples/general/permissions.ts +++ b/examples/general/permissions.ts @@ -3,7 +3,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const ACCOUNT_ID = "" -const client = new MailtrapClient({ token: TOKEN, accountId: ACCOUNT_ID }); +const client = new MailtrapClient({ token: TOKEN, accountId: Number(ACCOUNT_ID) }); const permissionsClient = client.general.permissions diff --git a/examples/sandbox/send-mail.ts b/examples/sandbox/send-mail.ts index 12a784bc..a49af13e 100644 --- a/examples/sandbox/send-mail.ts +++ b/examples/sandbox/send-mail.ts @@ -13,7 +13,7 @@ const SANDBOX_ID = "" const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; -const client = new MailtrapClient({ token: TOKEN, sandbox: true, sandboxId: SANDBOX_ID }); +const client = new MailtrapClient({ token: TOKEN, sandbox: true, sandboxId: Number(SANDBOX_ID) }); client.send({ from: { name: "Mailtrap Test", email: SENDER_EMAIL }, diff --git a/examples/sandbox/template.ts b/examples/sandbox/template.ts index 46f794bd..3df84095 100644 --- a/examples/sandbox/template.ts +++ b/examples/sandbox/template.ts @@ -11,7 +11,7 @@ const SANDBOX_ID = ""; const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; -const client = new MailtrapClient({ token: TOKEN, sandbox: true, sandboxId: SANDBOX_ID }); +const client = new MailtrapClient({ token: TOKEN, sandbox: true, sandboxId: Number(SANDBOX_ID) }); client.send({ from: { name: "Mailtrap Test", email: SENDER_EMAIL }, diff --git a/examples/sandbox/transport.ts b/examples/sandbox/transport.ts index dea53105..a2da0d49 100644 --- a/examples/sandbox/transport.ts +++ b/examples/sandbox/transport.ts @@ -15,7 +15,7 @@ const RECIPIENT_EMAIL = ""; const transport = Nodemailer.createTransport(MailtrapTransport({ token: TOKEN, - sandboxId: SANDBOX_ID, + sandboxId: Number(SANDBOX_ID), sandbox: true }))