Skip to content

Commit d98f1b2

Browse files
Merge pull request #68 from railsware/supressions-api
Suppressions api
2 parents c5f1d30 + 3ba3365 commit d98f1b2

21 files changed

+388
-12
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Refer to the [`examples`](examples) folder for the source code of this and other
7676
- [Advanced](examples/sending/everything.ts)
7777
- [Minimal](examples/sending/minimal.ts)
7878
- [Send email using template](examples/sending/template.ts)
79+
- [Suppressions](examples/sending/suppressions.ts)
7980
- [Nodemailer transport](examples/sending/transport.ts)
8081

8182
### Batch Sending API

examples/sending/suppressions.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { MailtrapClient } from "mailtrap";
2+
3+
const TOKEN = "<YOUR-TOKEN-HERE>";
4+
const ACCOUNT_ID = "<YOUR-ACCOUNT-ID-HERE>";
5+
6+
const client = new MailtrapClient({
7+
token: TOKEN,
8+
accountId: ACCOUNT_ID
9+
});
10+
11+
async function suppressionsFlow() {
12+
// Get suppressions (up to 1000 per request)
13+
const suppressions = await client.suppressions.getList();
14+
console.log("Suppressions (up to 1000):", suppressions);
15+
16+
// Get suppressions filtered by email
17+
const filteredSuppressions = await client.suppressions.getList({email: "test@example.com"});
18+
console.log("Filtered suppressions:", filteredSuppressions);
19+
20+
// Delete a suppression by ID (if any exist)
21+
if (suppressions.length > 0) {
22+
const suppressionToDelete = suppressions[0];
23+
await client.suppressions.delete(suppressionToDelete.id);
24+
console.log(`Suppression ${suppressionToDelete.id} deleted successfully`);
25+
} else {
26+
console.log("No suppressions found to delete");
27+
}
28+
}
29+
30+
suppressionsFlow().catch(console.error);

src/__tests__/lib/api/ContactLists.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("lib/api/ContactLists: ", () => {
88

99
describe("class ContactLists(): ", () => {
1010
describe("init: ", () => {
11-
it("initalizes with all necessary params.", () => {
11+
it("initializes with all necessary params.", () => {
1212
expect(contactListsAPI).toHaveProperty("create");
1313
expect(contactListsAPI).toHaveProperty("getList");
1414
expect(contactListsAPI).toHaveProperty("get");

src/__tests__/lib/api/Contacts.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("lib/api/Contacts: ", () => {
88

99
describe("class Contacts(): ", () => {
1010
describe("init: ", () => {
11-
it("initalizes with all necessary params.", () => {
11+
it("initializes with all necessary params.", () => {
1212
expect(contactsAPI).toHaveProperty("create");
1313
expect(contactsAPI).toHaveProperty("get");
1414
expect(contactsAPI).toHaveProperty("update");

src/__tests__/lib/api/General.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("lib/api/General: ", () => {
88

99
describe("class General(): ", () => {
1010
describe("init: ", () => {
11-
it("initalizes with all necessary params.", () => {
11+
it("initializes with all necessary params.", () => {
1212
expect(generalAPI).toHaveProperty("accountAccesses");
1313
expect(generalAPI).toHaveProperty("accounts");
1414
expect(generalAPI).toHaveProperty("permissions");
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import axios from "axios";
2+
3+
import SuppressionsBaseAPI from "../../../lib/api/Suppressions";
4+
5+
describe("lib/api/Suppressions: ", () => {
6+
const accountId = 100;
7+
const suppressionsAPI = new SuppressionsBaseAPI(axios, accountId);
8+
9+
describe("class SuppressionsBaseAPI(): ", () => {
10+
describe("init: ", () => {
11+
it("initializes with all necessary params.", () => {
12+
expect(suppressionsAPI).toHaveProperty("getList");
13+
expect(suppressionsAPI).toHaveProperty("delete");
14+
});
15+
});
16+
});
17+
});

src/__tests__/lib/api/Templates.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("lib/api/Templates: ", () => {
88

99
describe("class TemplatesBaseAPI(): ", () => {
1010
describe("init: ", () => {
11-
it("initalizes with all necessary params.", () => {
11+
it("initializes with all necessary params.", () => {
1212
expect(templatesAPI).toHaveProperty("create");
1313
expect(templatesAPI).toHaveProperty("getList");
1414
expect(templatesAPI).toHaveProperty("get");

src/__tests__/lib/api/Testing.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("lib/api/Testing: ", () => {
88

99
describe("class Testing(): ", () => {
1010
describe("init: ", () => {
11-
it("initalizes with all necessary params.", () => {
11+
it("initializes with all necessary params.", () => {
1212
expect(testingAPI).toHaveProperty("projects");
1313
expect(testingAPI).toHaveProperty("inboxes");
1414
expect(testingAPI).toHaveProperty("messages");

src/__tests__/lib/api/resources/AccountAccesses.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe("lib/api/resources/AccountAccesses: ", () => {
4242

4343
describe("class AccountAccesses(): ", () => {
4444
describe("init: ", () => {
45-
it("initalizes with all necessary params.", () => {
45+
it("initializes with all necessary params.", () => {
4646
expect(accountAccessesAPI).toHaveProperty("listAccountAccesses");
4747
expect(accountAccessesAPI).toHaveProperty("removeAccountAccess");
4848
});

src/__tests__/lib/api/resources/Accounts.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe("lib/api/resources/Accounts: ", () => {
2222

2323
describe("class Accounts(): ", () => {
2424
describe("init: ", () => {
25-
it("initalizes with all necessary params.", () => {
25+
it("initializes with all necessary params.", () => {
2626
expect(accountsAPI).toHaveProperty("getAllAccounts");
2727
});
2828
});

src/__tests__/lib/api/resources/Attachments.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe("lib/api/resources/Attachments: ", () => {
3434

3535
describe("class Attachments(): ", () => {
3636
describe("init: ", () => {
37-
it("initalizes with all necessary params.", () => {
37+
it("initializes with all necessary params.", () => {
3838
expect(attachmentsAPI).toHaveProperty("get");
3939
expect(attachmentsAPI).toHaveProperty("getList");
4040
});

src/__tests__/lib/api/resources/Inboxes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe("lib/api/resources/Inboxes: ", () => {
4242

4343
describe("class Inboxes(): ", () => {
4444
describe("init: ", () => {
45-
it("initalizes with all necessary params.", () => {
45+
it("initializes with all necessary params.", () => {
4646
expect(inboxesAPI).toHaveProperty("cleanInbox");
4747
expect(inboxesAPI).toHaveProperty("create");
4848
expect(inboxesAPI).toHaveProperty("delete");

src/__tests__/lib/api/resources/Messages.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe("lib/api/resources/Messages: ", () => {
4545

4646
describe("class Messages(): ", () => {
4747
describe("init: ", () => {
48-
it("initalizes with all necessary params.", () => {
48+
it("initializes with all necessary params.", () => {
4949
expect(messagesAPI).toHaveProperty("deleteMessage");
5050
expect(messagesAPI).toHaveProperty("forward");
5151
expect(messagesAPI).toHaveProperty("get");

src/__tests__/lib/api/resources/Permissions.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe("lib/api/resources/Permissions: ", () => {
2828

2929
describe("class Permissions(): ", () => {
3030
describe("init: ", () => {
31-
it("initalizes with all necessary params.", () => {
31+
it("initializes with all necessary params.", () => {
3232
expect(permissionsAPI).toHaveProperty("getResources");
3333
expect(permissionsAPI).toHaveProperty("bulkPermissionsUpdate");
3434
});

src/__tests__/lib/api/resources/Projects.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe("lib/api/resources/Projects: ", () => {
2424

2525
describe("class Projects(): ", () => {
2626
describe("init: ", () => {
27-
it("initalizes with all necessary params.", () => {
27+
it("initializes with all necessary params.", () => {
2828
expect(projectsAPI).toHaveProperty("create");
2929
expect(projectsAPI).toHaveProperty("delete");
3030
expect(projectsAPI).toHaveProperty("getById");
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import axios from "axios";
2+
import AxiosMockAdapter from "axios-mock-adapter";
3+
4+
import SuppressionsApi from "../../../../lib/api/resources/Suppressions";
5+
import handleSendingError from "../../../../lib/axios-logger";
6+
import MailtrapError from "../../../../lib/MailtrapError";
7+
import { Suppression } from "../../../../types/api/suppressions";
8+
9+
import CONFIG from "../../../../config";
10+
11+
const { CLIENT_SETTINGS } = CONFIG;
12+
const { GENERAL_ENDPOINT } = CLIENT_SETTINGS;
13+
14+
describe("lib/api/resources/Suppressions: ", () => {
15+
let mock: AxiosMockAdapter;
16+
const accountId = 100;
17+
const suppressionsAPI = new SuppressionsApi(axios, accountId);
18+
19+
const mockSuppression: Suppression = {
20+
id: "1",
21+
email: "test@example.com",
22+
type: "hard bounce",
23+
created_at: "2023-01-01T00:00:00Z",
24+
sending_stream: "transactional",
25+
domain_name: "example.com",
26+
message_bounce_category: "bad_mailbox",
27+
message_category: "test",
28+
message_client_ip: "192.168.1.1",
29+
message_created_at: "2023-01-01T00:00:00Z",
30+
message_outgoing_ip: "10.0.0.1",
31+
message_recipient_mx_name: "mx.example.com",
32+
message_sender_email: "sender@example.com",
33+
message_subject: "Test Email",
34+
};
35+
36+
const mockSuppressions: Suppression[] = [
37+
{
38+
id: "1",
39+
email: "test1@example.com",
40+
type: "hard bounce",
41+
created_at: "2023-01-01T00:00:00Z",
42+
sending_stream: "transactional",
43+
domain_name: "example.com",
44+
message_bounce_category: "bad_mailbox",
45+
message_category: "test",
46+
message_client_ip: "192.168.1.1",
47+
message_created_at: "2023-01-01T00:00:00Z",
48+
message_outgoing_ip: "10.0.0.1",
49+
message_recipient_mx_name: "mx.example.com",
50+
message_sender_email: "sender@example.com",
51+
message_subject: "Test Email 1",
52+
},
53+
{
54+
id: "2",
55+
email: "test2@example.com",
56+
type: "spam complaint",
57+
created_at: "2023-01-02T00:00:00Z",
58+
sending_stream: "bulk",
59+
domain_name: "example.com",
60+
message_bounce_category: null,
61+
message_category: "promotional",
62+
message_client_ip: "192.168.1.2",
63+
message_created_at: "2023-01-02T00:00:00Z",
64+
message_outgoing_ip: "10.0.0.2",
65+
message_recipient_mx_name: "mx.example.com",
66+
message_sender_email: "sender@example.com",
67+
message_subject: "Test Email 2",
68+
},
69+
];
70+
71+
describe("class SuppressionsApi(): ", () => {
72+
describe("init: ", () => {
73+
it("initializes with all necessary params.", () => {
74+
expect(suppressionsAPI).toHaveProperty("getList");
75+
expect(suppressionsAPI).toHaveProperty("delete");
76+
});
77+
});
78+
});
79+
80+
beforeAll(() => {
81+
axios.interceptors.response.use(
82+
(response) => response.data,
83+
handleSendingError
84+
);
85+
mock = new AxiosMockAdapter(axios);
86+
});
87+
88+
afterEach(() => {
89+
mock.reset();
90+
});
91+
92+
describe("getList(): ", () => {
93+
it("successfully gets suppressions (up to 1000 per request).", async () => {
94+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;
95+
96+
expect.assertions(2);
97+
98+
mock.onGet(endpoint).reply(200, mockSuppressions);
99+
const result = await suppressionsAPI.getList();
100+
101+
expect(mock.history.get[0].url).toEqual(endpoint);
102+
expect(result).toEqual(mockSuppressions);
103+
});
104+
105+
it("successfully gets suppressions filtered by email.", async () => {
106+
const email = "test@example.com";
107+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;
108+
109+
expect.assertions(3);
110+
111+
mock.onGet(endpoint, { params: { email } }).reply(200, [mockSuppression]);
112+
const result = await suppressionsAPI.getList({ email });
113+
114+
expect(mock.history.get[0].url).toEqual(endpoint);
115+
expect(mock.history.get[0].params).toEqual({ email });
116+
expect(result).toEqual([mockSuppression]);
117+
});
118+
119+
it("fails with unauthorized error (401).", async () => {
120+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;
121+
const expectedErrorMessage = "Incorrect API token";
122+
123+
expect.assertions(2);
124+
125+
mock.onGet(endpoint).reply(401, { error: expectedErrorMessage });
126+
127+
try {
128+
await suppressionsAPI.getList();
129+
} catch (error) {
130+
expect(error).toBeInstanceOf(MailtrapError);
131+
if (error instanceof MailtrapError) {
132+
expect(error.message).toEqual(expectedErrorMessage);
133+
}
134+
}
135+
});
136+
137+
it("fails with forbidden error (403).", async () => {
138+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions`;
139+
const expectedErrorMessage = "Access forbidden";
140+
141+
expect.assertions(2);
142+
143+
mock.onGet(endpoint).reply(403, { errors: expectedErrorMessage });
144+
145+
try {
146+
await suppressionsAPI.getList();
147+
} catch (error) {
148+
expect(error).toBeInstanceOf(MailtrapError);
149+
if (error instanceof MailtrapError) {
150+
expect(error.message).toEqual(expectedErrorMessage);
151+
}
152+
}
153+
});
154+
});
155+
156+
describe("delete(): ", () => {
157+
const suppressionId = "1";
158+
159+
it("successfully deletes a suppression.", async () => {
160+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`;
161+
162+
expect.assertions(1);
163+
164+
mock.onDelete(endpoint).reply(204);
165+
await suppressionsAPI.delete(suppressionId);
166+
167+
expect(mock.history.delete[0].url).toEqual(endpoint);
168+
});
169+
170+
it("fails with unauthorized error (401).", async () => {
171+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`;
172+
const expectedErrorMessage = "Incorrect API token";
173+
174+
expect.assertions(2);
175+
176+
mock.onDelete(endpoint).reply(401, { error: expectedErrorMessage });
177+
178+
try {
179+
await suppressionsAPI.delete(suppressionId);
180+
} catch (error) {
181+
expect(error).toBeInstanceOf(MailtrapError);
182+
if (error instanceof MailtrapError) {
183+
expect(error.message).toEqual(expectedErrorMessage);
184+
}
185+
}
186+
});
187+
188+
it("fails with forbidden error (403).", async () => {
189+
const endpoint = `${GENERAL_ENDPOINT}/api/accounts/${accountId}/suppressions/${suppressionId}`;
190+
const expectedErrorMessage = "Access forbidden";
191+
192+
expect.assertions(2);
193+
194+
mock.onDelete(endpoint).reply(403, { errors: expectedErrorMessage });
195+
196+
try {
197+
await suppressionsAPI.delete(suppressionId);
198+
} catch (error) {
199+
expect(error).toBeInstanceOf(MailtrapError);
200+
if (error instanceof MailtrapError) {
201+
expect(error.message).toEqual(expectedErrorMessage);
202+
}
203+
}
204+
});
205+
});
206+
});

0 commit comments

Comments
 (0)