diff --git a/examples/general/accounts.ts b/examples/general/accounts.ts index 73b795f2..9c73c551 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 }); +// Listing accounts does not need an accountId, so you can use it to find yours. +const client = new MailtrapClient({ token: TOKEN }); const accountsClient = client.general.accounts diff --git a/src/__tests__/lib/api/General.test.ts b/src/__tests__/lib/api/General.test.ts index 51928e23..53c1db1f 100644 --- a/src/__tests__/lib/api/General.test.ts +++ b/src/__tests__/lib/api/General.test.ts @@ -1,6 +1,11 @@ import axios from "axios"; import General from "../../../lib/api/General"; +import MailtrapError from "../../../lib/MailtrapError"; + +import CONFIG from "../../../config"; + +const { ACCOUNT_ID_MISSING } = CONFIG.ERRORS; describe("lib/api/General: ", () => { const testAccountId = 100; @@ -44,39 +49,23 @@ describe("lib/api/General: ", () => { const generalAPI = new General(axios); it("throws error when accessing accountAccesses without accountId.", () => { - expect(() => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - generalAPI.accountAccesses; - }).toThrow( - "Account ID is required for this operation. Please provide accountId when creating GeneralAPI instance." - ); + expect(() => generalAPI.accountAccesses).toThrow(MailtrapError); + expect(() => generalAPI.accountAccesses).toThrow(ACCOUNT_ID_MISSING); }); it("throws error when accessing permissions without accountId.", () => { - expect(() => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - generalAPI.permissions; - }).toThrow( - "Account ID is required for this operation. Please provide accountId when creating GeneralAPI instance." - ); + expect(() => generalAPI.permissions).toThrow(MailtrapError); + expect(() => generalAPI.permissions).toThrow(ACCOUNT_ID_MISSING); }); it("throws error when accessing billing without accountId.", () => { - expect(() => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - generalAPI.billing; - }).toThrow( - "Account ID is required for this operation. Please provide accountId when creating GeneralAPI instance." - ); + expect(() => generalAPI.billing).toThrow(MailtrapError); + expect(() => generalAPI.billing).toThrow(ACCOUNT_ID_MISSING); }); it("throws error when accessing apiTokens without accountId.", () => { - expect(() => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - generalAPI.apiTokens; - }).toThrow( - "Account ID is required for this operation. Please provide accountId when creating GeneralAPI instance." - ); + expect(() => generalAPI.apiTokens).toThrow(MailtrapError); + expect(() => generalAPI.apiTokens).toThrow(ACCOUNT_ID_MISSING); }); }); diff --git a/src/__tests__/lib/mailtrap-client.test.ts b/src/__tests__/lib/mailtrap-client.test.ts index c972d3f1..c3de9f76 100644 --- a/src/__tests__/lib/mailtrap-client.test.ts +++ b/src/__tests__/lib/mailtrap-client.test.ts @@ -21,7 +21,8 @@ import ContactEventsBaseAPI from "../../lib/api/ContactEvents"; import MarketingStatsBaseAPI from "../../lib/api/MarketingStats"; const { ERRORS, CLIENT_SETTINGS } = CONFIG; -const { TESTING_ENDPOINT, BULK_ENDPOINT, SENDING_ENDPOINT } = CLIENT_SETTINGS; +const { TESTING_ENDPOINT, BULK_ENDPOINT, SENDING_ENDPOINT, GENERAL_ENDPOINT } = + CLIENT_SETTINGS; const { ACCOUNT_ID_MISSING, BULK_SANDBOX_INCOMPATIBLE, TEST_INBOX_ID_MISSING } = ERRORS; @@ -751,28 +752,57 @@ describe("lib/mailtrap-client: ", () => { }); describe("get general(): ", () => { - it("rejects with Mailtrap error, when `accountId` is missing.", () => { - const client = new MailtrapClient({ - token: "MY_API_TOKEN", - }); - expect.assertions(1); + it("returns general API object with accounts, but rejects account-scoped resources, when `accountId` is missing.", async () => { + const accountsMock = new AxiosMockAdapter(axios); + const accounts = [{ id: 1, name: "Account", access_levels: [1000] }]; + accountsMock + .onGet(`${GENERAL_ENDPOINT}/api/accounts`) + .reply(200, accounts); try { - client.general; - } catch (error) { - expect(error).toEqual(new MailtrapError(ACCOUNT_ID_MISSING)); + const { general } = new MailtrapClient({ token: "MY_API_TOKEN" }); + + expect(general).toBeInstanceOf(GeneralAPI); + await expect(general.accounts.getAllAccounts()).resolves.toEqual( + accounts + ); + expect(() => general.billing).toThrow(MailtrapError); + expect(() => general.billing).toThrow(ACCOUNT_ID_MISSING); + } finally { + accountsMock.restore(); } }); - it("returns general API object when accountId is provided.", () => { - const client = new MailtrapClient({ + it("rejects account-scoped resources, when `accountId` is 0.", () => { + const { general } = new MailtrapClient({ token: "MY_API_TOKEN", - accountId: 10, + accountId: 0, }); - expect.assertions(1); - const generalClient = client.general; - expect(generalClient).toBeInstanceOf(GeneralAPI); + expect(() => general.billing).toThrow(MailtrapError); + expect(() => general.billing).toThrow(ACCOUNT_ID_MISSING); + }); + + it("returns general API object scoped to the provided accountId.", async () => { + const billingMock = new AxiosMockAdapter(axios); + const usage = { billing: { cycle_start: "2026-11-01" } }; + billingMock + .onGet(`${GENERAL_ENDPOINT}/api/accounts/10/billing/usage`) + .reply(200, usage); + + try { + const { general } = new MailtrapClient({ + token: "MY_API_TOKEN", + accountId: 10, + }); + + expect(general).toBeInstanceOf(GeneralAPI); + await expect( + general.billing.getCurrentBillingCycleUsage() + ).resolves.toEqual(usage); + } finally { + billingMock.restore(); + } }); }); diff --git a/src/lib/MailtrapClient.ts b/src/lib/MailtrapClient.ts index 9638a5a9..5a96750a 100644 --- a/src/lib/MailtrapClient.ts +++ b/src/lib/MailtrapClient.ts @@ -146,11 +146,10 @@ export default class MailtrapClient { } /** - * Getter for General API. + * Getter for General API. `accounts` works without accountId; other resources require it. */ get general() { - const accountId = this.validateAccountIdPresence(); - return new GeneralAPI(this.axios, accountId); + return new GeneralAPI(this.axios, this.accountId || undefined); } /** diff --git a/src/lib/api/General.ts b/src/lib/api/General.ts index 0a20e071..38fa9c08 100644 --- a/src/lib/api/General.ts +++ b/src/lib/api/General.ts @@ -1,11 +1,15 @@ import { AxiosInstance } from "axios"; +import CONFIG from "../../config"; +import MailtrapError from "../MailtrapError"; import AccountAccessesApi from "./resources/AccountAccesses"; import AccountsApi from "./resources/Accounts"; import ApiTokensApi from "./resources/ApiTokens"; import BillingApi from "./resources/Billing"; import PermissionsApi from "./resources/Permissions"; +const { ACCOUNT_ID_MISSING } = CONFIG.ERRORS; + export default class GeneralAPI { public accounts: AccountsApi; @@ -41,9 +45,7 @@ export default class GeneralAPI { */ private checkAccountIdPresence(): number { if (this.accountId === null) { - throw new Error( - "Account ID is required for this operation. Please provide accountId when creating GeneralAPI instance." - ); + throw new MailtrapError(ACCOUNT_ID_MISSING); } return this.accountId; }