diff --git a/README.md b/README.md index 77bfea1..3ed1c57 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,8 @@ 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 – [`sending-domains/everything.ts`](examples/sending-domains/everything.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) - 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) diff --git a/examples/company-info/everything.ts b/examples/company-info/everything.ts new file mode 100644 index 0000000..b34de56 --- /dev/null +++ b/examples/company-info/everything.ts @@ -0,0 +1,43 @@ +import { MailtrapClient } from "mailtrap"; + +const TOKEN = ""; +const DOMAIN_ID = Number(""); + +const client = new MailtrapClient({ token: TOKEN }); + +async function companyInfoFlow() { + try { + // Create the company info required for compliance verification + const created = await client.companyInfo.create(DOMAIN_ID, { + name: "Mailtrap", + address: "123 Main St", + city: "San Francisco", + country: "US", + zip_code: "94105", + website_url: "https://mailtrap.io", + phone: "+1-555-0100", + privacy_policy_url: "https://mailtrap.io/privacy", + terms_of_service_url: "https://mailtrap.io/terms", + info_level: "business", + }); + console.log("Created company info:", JSON.stringify(created, null, 2)); + + // Get the company info of a sending domain + const companyInfo = await client.companyInfo.get(DOMAIN_ID); + console.log("Company info:", JSON.stringify(companyInfo, null, 2)); + + // Update only some of the fields + const updated = await client.companyInfo.update(DOMAIN_ID, { + city: "New York", + zip_code: "10001", + }); + console.log("Updated company info:", JSON.stringify(updated, null, 2)); + } catch (error) { + console.error( + "Error in companyInfoFlow:", + error instanceof Error ? error.message : String(error) + ); + } +} + +companyInfoFlow(); diff --git a/examples/sending-domains/everything.ts b/examples/sending-domains/everything.ts index f0d56fb..4fb7e14 100644 --- a/examples/sending-domains/everything.ts +++ b/examples/sending-domains/everything.ts @@ -35,7 +35,17 @@ async function sendingDomainsFlow() { 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"); diff --git a/src/__tests__/lib/api/resources/CompanyInfo.test.ts b/src/__tests__/lib/api/resources/CompanyInfo.test.ts new file mode 100644 index 0000000..fe27a10 --- /dev/null +++ b/src/__tests__/lib/api/resources/CompanyInfo.test.ts @@ -0,0 +1,109 @@ +import axios, { AxiosInstance } from "axios"; +import MockAdapter from "axios-mock-adapter"; + +import CompanyInfoBaseAPI from "../../../../lib/api/CompanyInfo"; +import { CompanyInfo } from "../../../../types/api/company-info"; + +describe("lib/api/CompanyInfo: ", () => { + const axiosInstance: AxiosInstance = axios.create(); + const mock = new MockAdapter(axiosInstance); + + // Add the response interceptor that returns response.data + axiosInstance.interceptors.response.use((response) => response.data); + + const companyInfoAPI = new CompanyInfoBaseAPI(axiosInstance); + const domainId = 999; + const companyInfoURL = `https://mailtrap.io/api/domains/${domainId}/company_info`; + + describe("class CompanyInfoBaseAPI(): ", () => { + describe("init: ", () => { + it("initializes with all necessary params.", () => { + expect(companyInfoAPI).toHaveProperty("get"); + expect(companyInfoAPI).toHaveProperty("create"); + expect(companyInfoAPI).toHaveProperty("update"); + }); + }); + + describe("companyInfo.get(): ", () => { + it("should get the company info of a sending domain.", async () => { + const mockCompanyInfo: CompanyInfo = { + name: "Mailtrap", + address: "123 Main St", + city: "San Francisco", + country: "US", + phone: "+1-555-0100", + zip_code: "94105", + privacy_policy_url: "https://mailtrap.io/privacy", + terms_of_service_url: "https://mailtrap.io/terms", + website_url: "https://mailtrap.io", + info_level: "business", + }; + + mock.onGet(companyInfoURL).reply(200, { data: mockCompanyInfo }); + + const result = await companyInfoAPI.get(domainId); + + expect(result).toEqual({ data: mockCompanyInfo }); + }); + }); + + describe("companyInfo.create(): ", () => { + it("should create the company info of a sending domain.", async () => { + const createParams = { + name: "Mailtrap", + address: "123 Main St", + city: "San Francisco", + country: "US", + zip_code: "94105", + website_url: "https://mailtrap.io", + info_level: "business" as const, + }; + + const mockCompanyInfo: CompanyInfo = { + ...createParams, + phone: null, + privacy_policy_url: null, + terms_of_service_url: null, + }; + + mock + .onPost(companyInfoURL, { company_info: createParams }) + .reply(200, { data: mockCompanyInfo }); + + const result = await companyInfoAPI.create(domainId, createParams); + + expect(result).toEqual({ data: mockCompanyInfo }); + }); + }); + + describe("companyInfo.update(): ", () => { + it("should update the company info of a sending domain.", async () => { + const updateParams = { + city: "New York", + zip_code: "10001", + }; + + const mockCompanyInfo: CompanyInfo = { + name: "Mailtrap", + address: "123 Main St", + city: "New York", + country: "US", + phone: null, + zip_code: "10001", + privacy_policy_url: null, + terms_of_service_url: null, + website_url: "https://mailtrap.io", + info_level: "business", + }; + + mock + .onPatch(companyInfoURL, { company_info: updateParams }) + .reply(200, { data: mockCompanyInfo }); + + const result = await companyInfoAPI.update(domainId, updateParams); + + expect(result).toEqual({ data: mockCompanyInfo }); + }); + }); + }); +}); diff --git a/src/__tests__/lib/api/resources/SendingDomains.test.ts b/src/__tests__/lib/api/resources/SendingDomains.test.ts index caa0f0f..ee16248 100644 --- a/src/__tests__/lib/api/resources/SendingDomains.test.ts +++ b/src/__tests__/lib/api/resources/SendingDomains.test.ts @@ -27,6 +27,7 @@ describe("lib/api/SendingDomains: ", () => { expect(sendingDomainsAPI).toHaveProperty("get"); expect(sendingDomainsAPI).toHaveProperty("getList"); expect(sendingDomainsAPI).toHaveProperty("create"); + expect(sendingDomainsAPI).toHaveProperty("update"); expect(sendingDomainsAPI).toHaveProperty("delete"); expect(sendingDomainsAPI).toHaveProperty("sendSetupInstructions"); }); @@ -70,6 +71,7 @@ describe("lib/api/SendingDomains: ", () => { dns_records: mockDnsRecords, open_tracking_enabled: true, click_tracking_enabled: true, + tracking_opt_out_enabled: false, auto_unsubscribe_link_enabled: true, custom_domain_tracking_enabled: true, health_alerts_enabled: true, @@ -122,6 +124,7 @@ describe("lib/api/SendingDomains: ", () => { dns_records: mockDnsRecords, open_tracking_enabled: true, click_tracking_enabled: true, + tracking_opt_out_enabled: false, auto_unsubscribe_link_enabled: true, custom_domain_tracking_enabled: true, health_alerts_enabled: true, @@ -173,6 +176,7 @@ describe("lib/api/SendingDomains: ", () => { dns_records: mockDnsRecords, open_tracking_enabled: true, click_tracking_enabled: true, + tracking_opt_out_enabled: false, auto_unsubscribe_link_enabled: true, custom_domain_tracking_enabled: true, health_alerts_enabled: true, @@ -200,6 +204,59 @@ describe("lib/api/SendingDomains: ", () => { }); }); + describe("sendingDomains.update(): ", () => { + it("should update a sending domain.", async () => { + const mockPermissions: SendingDomainPermissions = { + can_read: true, + can_update: true, + can_destroy: true, + }; + + const mockSendingDomain: SendingDomain = { + id: 435, + domain_name: "example.com", + demo: false, + compliance_status: "compliant", + dns_verified: true, + dns_verified_at: "2024-12-26T09:40:44.161Z", + dns_records: [], + open_tracking_enabled: true, + click_tracking_enabled: true, + tracking_opt_out_enabled: true, + auto_unsubscribe_link_enabled: false, + custom_domain_tracking_enabled: true, + health_alerts_enabled: true, + critical_alerts_enabled: true, + alert_recipient_email: "john.doe@example.com", + inbound_enabled: true, + inbound_verified: true, + permissions: mockPermissions, + }; + + const updateParams = { + open_tracking_enabled: true, + click_tracking_enabled: true, + tracking_opt_out_enabled: true, + auto_unsubscribe_link_enabled: false, + inbound_enabled: true, + }; + + mock + .onPatch( + `https://mailtrap.io/api/accounts/${testAccountId}/sending_domains/${mockSendingDomain.id}`, + { sending_domain: updateParams } + ) + .reply(200, mockSendingDomain); + + const result = await sendingDomainsAPI.update( + mockSendingDomain.id, + updateParams + ); + + expect(result).toEqual(mockSendingDomain); + }); + }); + describe("sendingDomains.delete(): ", () => { it("should delete a sending domain by id.", async () => { const sendingDomainId = 999; diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index ee272d3..80f20c9 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -7,6 +7,7 @@ import encodeMailBuffers from "./mail-buffer-encoder"; import handleSendingError from "./axios-logger"; import MailtrapError from "./MailtrapError"; +import CompanyInfoBaseAPI from "./api/CompanyInfo"; import ContactEventsBaseAPI from "./api/ContactEvents"; import ContactExportsBaseAPI from "./api/ContactExports"; import ContactFieldsBaseAPI from "./api/ContactFields"; @@ -259,6 +260,13 @@ export default class MailtrapClient { return new EmailCampaignsBaseAPI(this.axios); } + /** + * Getter for Company Info API. + */ + get companyInfo() { + return new CompanyInfoBaseAPI(this.axios); + } + /** * Getter for Organizations API. Requires `organizationId` in config. */ diff --git a/src/lib/api/CompanyInfo.ts b/src/lib/api/CompanyInfo.ts new file mode 100644 index 0000000..2cf39b0 --- /dev/null +++ b/src/lib/api/CompanyInfo.ts @@ -0,0 +1,21 @@ +import { AxiosInstance } from "axios"; + +import CompanyInfoApi from "./resources/CompanyInfo"; + +export default class CompanyInfoBaseAPI { + private client: AxiosInstance; + + public get: CompanyInfoApi["get"]; + + public create: CompanyInfoApi["create"]; + + public update: CompanyInfoApi["update"]; + + constructor(client: AxiosInstance) { + this.client = client; + const companyInfo = new CompanyInfoApi(this.client); + this.get = companyInfo.get.bind(companyInfo); + this.create = companyInfo.create.bind(companyInfo); + this.update = companyInfo.update.bind(companyInfo); + } +} diff --git a/src/lib/api/SendingDomains.ts b/src/lib/api/SendingDomains.ts index db1f623..0810bca 100644 --- a/src/lib/api/SendingDomains.ts +++ b/src/lib/api/SendingDomains.ts @@ -11,6 +11,8 @@ export default class SendingDomainsBaseAPI { public create: SendingDomainsApi["create"]; + public update: SendingDomainsApi["update"]; + public delete: SendingDomainsApi["delete"]; public sendSetupInstructions: SendingDomainsApi["sendSetupInstructions"]; @@ -21,6 +23,7 @@ export default class SendingDomainsBaseAPI { 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); diff --git a/src/lib/api/resources/CompanyInfo.ts b/src/lib/api/resources/CompanyInfo.ts new file mode 100644 index 0000000..03468a0 --- /dev/null +++ b/src/lib/api/resources/CompanyInfo.ts @@ -0,0 +1,71 @@ +import { AxiosInstance } from "axios"; + +import CONFIG from "../../../config"; +import { + CompanyInfoResponse, + CreateCompanyInfoParams, + UpdateCompanyInfoParams, +} from "../../../types/api/company-info"; + +const { CLIENT_SETTINGS } = CONFIG; +const { GENERAL_ENDPOINT } = CLIENT_SETTINGS; + +export default class CompanyInfoApi { + private client: AxiosInstance; + + private domainsURL: string; + + constructor(client: AxiosInstance) { + this.client = client; + this.domainsURL = `${GENERAL_ENDPOINT}/api/domains`; + } + + /** + * Get the company info associated with a sending domain. + * @param domainId Sending domain ID + * @returns Returns the company info of the sending domain + */ + public async get(domainId: number) { + const url = this.companyInfoURL(domainId); + + return this.client.get(url); + } + + /** + * Create the company info for a sending domain. Company info is required for + * domain compliance verification. + * @param domainId Sending domain ID + * @param params Company info attributes + * @returns Returns the created company info + */ + public async create(domainId: number, params: CreateCompanyInfoParams) { + const url = this.companyInfoURL(domainId); + const data = { company_info: params }; + + return this.client.post( + url, + data + ); + } + + /** + * Update the company info for a sending domain. Only the fields provided are + * updated. + * @param domainId Sending domain ID + * @param params Company info attributes to update + * @returns Returns the updated company info + */ + public async update(domainId: number, params: UpdateCompanyInfoParams) { + const url = this.companyInfoURL(domainId); + const data = { company_info: params }; + + return this.client.patch( + url, + data + ); + } + + private companyInfoURL(domainId: number) { + return `${this.domainsURL}/${domainId}/company_info`; + } +} diff --git a/src/lib/api/resources/SendingDomains.ts b/src/lib/api/resources/SendingDomains.ts index d95b041..5c39b6e 100644 --- a/src/lib/api/resources/SendingDomains.ts +++ b/src/lib/api/resources/SendingDomains.ts @@ -5,6 +5,7 @@ import { CreateSendingDomainParams, SendingDomain, SendingDomainsResponse, + UpdateSendingDomainParams, } from "../../../types/api/sending-domains"; const { CLIENT_SETTINGS } = CONFIG; @@ -51,6 +52,19 @@ export default class SendingDomainsApi { 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 diff --git a/src/types/api/company-info.ts b/src/types/api/company-info.ts new file mode 100644 index 0000000..4f7e782 --- /dev/null +++ b/src/types/api/company-info.ts @@ -0,0 +1,33 @@ +export type CompanyInfoLevel = "business" | "individual"; + +export interface CompanyInfo { + name: string | null; + address: string | null; + city: string | null; + country: string | null; + phone: string | null; + zip_code: string | null; + privacy_policy_url: string | null; + terms_of_service_url: string | null; + website_url: string | null; + info_level: CompanyInfoLevel; +} + +export interface CreateCompanyInfoParams { + name: string; + address: string; + city: string; + country: string; + zip_code: string; + website_url: string; + phone?: string; + privacy_policy_url?: string; + terms_of_service_url?: string; + info_level?: CompanyInfoLevel; +} + +export type UpdateCompanyInfoParams = Partial; + +export interface CompanyInfoResponse { + data: CompanyInfo; +} diff --git a/src/types/api/sending-domains.ts b/src/types/api/sending-domains.ts index 567e435..5a9947f 100644 --- a/src/types/api/sending-domains.ts +++ b/src/types/api/sending-domains.ts @@ -23,6 +23,7 @@ export interface SendingDomain { 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; @@ -40,3 +41,11 @@ export interface CreateSendingDomainParams { export interface SendingDomainsResponse { data: SendingDomain[]; } + +export interface UpdateSendingDomainParams { + open_tracking_enabled?: boolean; + click_tracking_enabled?: boolean; + tracking_opt_out_enabled?: boolean; + auto_unsubscribe_link_enabled?: boolean; + inbound_enabled?: boolean; +}