Skip to content

Suppressions api #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jul 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
66ef0e4
docs: add Suppressions example to README for enhanced API usage clarity
narekhovhannisyan Jun 21, 2025
3171823
examples: add suppressions example to demonstrate MailtrapClient func…
narekhovhannisyan Jun 21, 2025
4c8580b
test: add unit tests for MailtrapClient suppressions getter to valida…
narekhovhannisyan Jun 21, 2025
f0e3907
test: add unit tests for SuppressionsBaseAPI to verify initialization…
narekhovhannisyan Jun 21, 2025
5f88ec2
test: add comprehensive unit tests for SuppressionsApi to validate ge…
narekhovhannisyan Jun 21, 2025
4912a5b
feat: add suppressions getter to MailtrapClient for managing suppress…
narekhovhannisyan Jun 21, 2025
f576198
feat: implement SuppressionsBaseAPI for managing suppressions with Ax…
narekhovhannisyan Jun 21, 2025
43a42bd
feat: create SuppressionsApi class for managing suppressions with Axi…
narekhovhannisyan Jun 21, 2025
1520fdb
feat: add Suppression type definition for managing suppression data s…
narekhovhannisyan Jun 21, 2025
8e47f33
Merge branch 'template-api-integration' of github.com:railsware/mailt…
narekhovhannisyan Jun 29, 2025
13b8ad9
test: rename supression test to supressions
narekhovhannisyan Jul 1, 2025
a2c3e6e
refactor: update getList method in SuppressionsApi to accept ListOpti…
narekhovhannisyan Jul 1, 2025
a8ec09e
feat: add ListOptions type to enhance flexibility in suppression queries
narekhovhannisyan Jul 1, 2025
bdc8e9e
Merge branch 'main' of github.com:railsware/mailtrap-nodejs into supr…
narekhovhannisyan Jul 2, 2025
88bff56
test: update Suppressions API test to pass email as an object
narekhovhannisyan Jul 2, 2025
70541b5
refactor: update suppressions flow to improve clarity and use object …
narekhovhannisyan Jul 3, 2025
8c28b49
test: enhance Suppressions API tests to specify error types and impro…
narekhovhannisyan Jul 3, 2025
9312aa2
refactor: streamline suppressions getter by introducing account ID va…
narekhovhannisyan Jul 4, 2025
3ba3365
fix: correct spelling of "initializes" in multiple test files
narekhovhannisyan Jul 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Refer to the [`examples`](examples) folder for the source code of this and other
- [Advanced](examples/sending/everything.ts)
- [Minimal](examples/sending/minimal.ts)
- [Send email using template](examples/sending/template.ts)
- [Suppressions](examples/sending/suppressions.ts)
- [Nodemailer transport](examples/sending/transport.ts)

### Batch Sending API
Expand Down
30 changes: 30 additions & 0 deletions examples/sending/suppressions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MailtrapClient } from "mailtrap";

const TOKEN = "<YOUR-TOKEN-HERE>";
const ACCOUNT_ID = "<YOUR-ACCOUNT-ID-HERE>";

const client = new MailtrapClient({
token: TOKEN,
accountId: ACCOUNT_ID
});

async function suppressionsFlow() {
// Get suppressions (up to 1000 per request)
const suppressions = await client.suppressions.getList();
console.log("Suppressions (up to 1000):", suppressions);

// Get suppressions filtered by email
const filteredSuppressions = await client.suppressions.getList({email: "test@example.com"});
console.log("Filtered suppressions:", filteredSuppressions);

// Delete a suppression by ID (if any exist)
if (suppressions.length > 0) {
const suppressionToDelete = suppressions[0];
await client.suppressions.delete(suppressionToDelete.id);
console.log(`Suppression ${suppressionToDelete.id} deleted successfully`);
} else {
console.log("No suppressions found to delete");
}
}

suppressionsFlow().catch(console.error);
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/ContactLists.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("lib/api/ContactLists: ", () => {

describe("class ContactLists(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(contactListsAPI).toHaveProperty("create");
expect(contactListsAPI).toHaveProperty("getList");
expect(contactListsAPI).toHaveProperty("get");
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/Contacts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("lib/api/Contacts: ", () => {

describe("class Contacts(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(contactsAPI).toHaveProperty("create");
expect(contactsAPI).toHaveProperty("get");
expect(contactsAPI).toHaveProperty("update");
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/General.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("lib/api/General: ", () => {

describe("class General(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(generalAPI).toHaveProperty("accountAccesses");
expect(generalAPI).toHaveProperty("accounts");
expect(generalAPI).toHaveProperty("permissions");
Expand Down
17 changes: 17 additions & 0 deletions src/__tests__/lib/api/Suppressions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import axios from "axios";

import SuppressionsBaseAPI from "../../../lib/api/Suppressions";

describe("lib/api/Suppressions: ", () => {
const accountId = 100;
const suppressionsAPI = new SuppressionsBaseAPI(axios, accountId);

describe("class SuppressionsBaseAPI(): ", () => {
describe("init: ", () => {
it("initializes with all necessary params.", () => {
expect(suppressionsAPI).toHaveProperty("getList");
expect(suppressionsAPI).toHaveProperty("delete");
});
});
});
});
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/Templates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("lib/api/Templates: ", () => {

describe("class TemplatesBaseAPI(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(templatesAPI).toHaveProperty("create");
expect(templatesAPI).toHaveProperty("getList");
expect(templatesAPI).toHaveProperty("get");
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/Testing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("lib/api/Testing: ", () => {

describe("class Testing(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(testingAPI).toHaveProperty("projects");
expect(testingAPI).toHaveProperty("inboxes");
expect(testingAPI).toHaveProperty("messages");
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/AccountAccesses.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("lib/api/resources/AccountAccesses: ", () => {

describe("class AccountAccesses(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(accountAccessesAPI).toHaveProperty("listAccountAccesses");
expect(accountAccessesAPI).toHaveProperty("removeAccountAccess");
});
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/Accounts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe("lib/api/resources/Accounts: ", () => {

describe("class Accounts(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(accountsAPI).toHaveProperty("getAllAccounts");
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/Attachments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe("lib/api/resources/Attachments: ", () => {

describe("class Attachments(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(attachmentsAPI).toHaveProperty("get");
expect(attachmentsAPI).toHaveProperty("getList");
});
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/Inboxes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("lib/api/resources/Inboxes: ", () => {

describe("class Inboxes(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(inboxesAPI).toHaveProperty("cleanInbox");
expect(inboxesAPI).toHaveProperty("create");
expect(inboxesAPI).toHaveProperty("delete");
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/Messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("lib/api/resources/Messages: ", () => {

describe("class Messages(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(messagesAPI).toHaveProperty("deleteMessage");
expect(messagesAPI).toHaveProperty("forward");
expect(messagesAPI).toHaveProperty("get");
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/Permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe("lib/api/resources/Permissions: ", () => {

describe("class Permissions(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(permissionsAPI).toHaveProperty("getResources");
expect(permissionsAPI).toHaveProperty("bulkPermissionsUpdate");
});
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/lib/api/resources/Projects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe("lib/api/resources/Projects: ", () => {

describe("class Projects(): ", () => {
describe("init: ", () => {
it("initalizes with all necessary params.", () => {
it("initializes with all necessary params.", () => {
expect(projectsAPI).toHaveProperty("create");
expect(projectsAPI).toHaveProperty("delete");
expect(projectsAPI).toHaveProperty("getById");
Expand Down
206 changes: 206 additions & 0 deletions src/__tests__/lib/api/resources/Suppressions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import axios from "axios";
import AxiosMockAdapter from "axios-mock-adapter";

import SuppressionsApi from "../../../../lib/api/resources/Suppressions";
import handleSendingError from "../../../../lib/axios-logger";
import MailtrapError from "../../../../lib/MailtrapError";
import { Suppression } from "../../../../types/api/suppressions";

import CONFIG from "../../../../config";

const { CLIENT_SETTINGS } = CONFIG;
const { GENERAL_ENDPOINT } = CLIENT_SETTINGS;

describe("lib/api/resources/Suppressions: ", () => {
let mock: AxiosMockAdapter;
const accountId = 100;
const suppressionsAPI = new SuppressionsApi(axios, accountId);

const mockSuppression: Suppression = {
id: "1",
email: "test@example.com",
type: "hard bounce",
created_at: "2023-01-01T00:00:00Z",
sending_stream: "transactional",
domain_name: "example.com",
message_bounce_category: "bad_mailbox",
message_category: "test",
message_client_ip: "192.168.1.1",
message_created_at: "2023-01-01T00:00:00Z",
message_outgoing_ip: "10.0.0.1",
message_recipient_mx_name: "mx.example.com",
message_sender_email: "sender@example.com",
message_subject: "Test Email",
};

const mockSuppressions: Suppression[] = [
{
id: "1",
email: "test1@example.com",
type: "hard bounce",
created_at: "2023-01-01T00:00:00Z",
sending_stream: "transactional",
domain_name: "example.com",
message_bounce_category: "bad_mailbox",
message_category: "test",
message_client_ip: "192.168.1.1",
message_created_at: "2023-01-01T00:00:00Z",
message_outgoing_ip: "10.0.0.1",
message_recipient_mx_name: "mx.example.com",
message_sender_email: "sender@example.com",
message_subject: "Test Email 1",
},
{
id: "2",
email: "test2@example.com",
type: "spam complaint",
created_at: "2023-01-02T00:00:00Z",
sending_stream: "bulk",
domain_name: "example.com",
message_bounce_category: null,
message_category: "promotional",
message_client_ip: "192.168.1.2",
message_created_at: "2023-01-02T00:00:00Z",
message_outgoing_ip: "10.0.0.2",
message_recipient_mx_name: "mx.example.com",
message_sender_email: "sender@example.com",
message_subject: "Test Email 2",
},
];

describe("class SuppressionsApi(): ", () => {
describe("init: ", () => {
it("initializes with all necessary params.", () => {
expect(suppressionsAPI).toHaveProperty("getList");
expect(suppressionsAPI).toHaveProperty("delete");
});
});
});

beforeAll(() => {
axios.interceptors.response.use(
(response) => response.data,
handleSendingError
);
mock = new AxiosMockAdapter(axios);
});

afterEach(() => {
mock.reset();
});

describe("getList(): ", () => {
it("successfully gets suppressions (up to 1000 per request).", async () => {
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;

expect.assertions(2);

mock.onGet(endpoint).reply(200, mockSuppressions);
const result = await suppressionsAPI.getList();

expect(mock.history.get[0].url).toEqual(endpoint);
expect(result).toEqual(mockSuppressions);
});

it("successfully gets suppressions filtered by email.", async () => {
const email = "test@example.com";
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;

expect.assertions(3);

mock.onGet(endpoint, { params: { email } }).reply(200, [mockSuppression]);
const result = await suppressionsAPI.getList({ email });

expect(mock.history.get[0].url).toEqual(endpoint);
expect(mock.history.get[0].params).toEqual({ email });
expect(result).toEqual([mockSuppression]);
});

it("fails with unauthorized error (401).", async () => {
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;
const expectedErrorMessage = "Incorrect API token";

expect.assertions(2);

mock.onGet(endpoint).reply(401, { error: expectedErrorMessage });

try {
await suppressionsAPI.getList();
} catch (error) {
expect(error).toBeInstanceOf(MailtrapError);
if (error instanceof MailtrapError) {
expect(error.message).toEqual(expectedErrorMessage);
}
}
});

it("fails with forbidden error (403).", async () => {
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;
const expectedErrorMessage = "Access forbidden";

expect.assertions(2);

mock.onGet(endpoint).reply(403, { errors: expectedErrorMessage });

try {
await suppressionsAPI.getList();
} catch (error) {
expect(error).toBeInstanceOf(MailtrapError);
if (error instanceof MailtrapError) {
expect(error.message).toEqual(expectedErrorMessage);
}
}
});
});

describe("delete(): ", () => {
const suppressionId = "1";

it("successfully deletes a suppression.", async () => {
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`;

expect.assertions(1);

mock.onDelete(endpoint).reply(204);
await suppressionsAPI.delete(suppressionId);

expect(mock.history.delete[0].url).toEqual(endpoint);
});

it("fails with unauthorized error (401).", async () => {
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`;
const expectedErrorMessage = "Incorrect API token";

expect.assertions(2);

mock.onDelete(endpoint).reply(401, { error: expectedErrorMessage });

try {
await suppressionsAPI.delete(suppressionId);
} catch (error) {
expect(error).toBeInstanceOf(MailtrapError);
if (error instanceof MailtrapError) {
expect(error.message).toEqual(expectedErrorMessage);
}
}
});

it("fails with forbidden error (403).", async () => {
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`;
const expectedErrorMessage = "Access forbidden";

expect.assertions(2);

mock.onDelete(endpoint).reply(403, { errors: expectedErrorMessage });

try {
await suppressionsAPI.delete(suppressionId);
} catch (error) {
expect(error).toBeInstanceOf(MailtrapError);
if (error instanceof MailtrapError) {
expect(error.message).toEqual(expectedErrorMessage);
}
}
});
});
});
Loading