Skip to content

Commit aa98c10

Browse files
committed
Add constructor type and require baseUrl
1 parent 235eec9 commit aa98c10

File tree

5 files changed

+107
-4
lines changed

5 files changed

+107
-4
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"engines": {
2626
"node": ">=18.0.0"
2727
},
28-
"packageManager": "pnpm@10.22.0",
28+
"packageManager": "pnpm@10.23.0",
2929
"publishConfig": {
3030
"access": "public"
3131
},

src/client/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class BaseIterableClient {
5858
};
5959

6060
this.client = axios.create({
61-
baseURL: clientConfig.baseUrl || "https://api.iterable.com",
61+
baseURL: clientConfig.baseUrl,
6262
headers: {
6363
...defaultHeaders,
6464
...(clientConfig.customHeaders || {}),

src/client/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type { AxiosInstance } from "axios";
2+
3+
import type { IterableConfig } from "../types/common.js";
14
import { BaseIterableClient } from "./base.js";
25
import { Campaigns } from "./campaigns.js";
36
import { Catalogs } from "./catalogs.js";
@@ -62,4 +65,14 @@ export class IterableClient extends compose(
6265
Templates,
6366
Users,
6467
Webhooks
65-
) {}
68+
) {
69+
/**
70+
* Create a new Iterable API client
71+
*
72+
* @param config - Optional configuration object. If not provided, will use environment variables
73+
* @param injectedClient - Optional pre-configured Axios instance for testing
74+
*/
75+
constructor(config?: IterableConfig, injectedClient?: AxiosInstance) {
76+
super(config, injectedClient);
77+
}
78+
}

src/types/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export type IterableErrorResponse = z.infer<typeof IterableErrorResponseSchema>;
107107

108108
export const IterableConfigSchema = z.object({
109109
apiKey: z.string(),
110-
baseUrl: z.string().optional(),
110+
baseUrl: z.url(),
111111
timeout: z.number().optional(),
112112
debug: z.boolean().optional(),
113113
debugVerbose: z.boolean().optional(),

tests/unit/client.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Tests for IterableClient constructor - verifies that baseUrl is required
3+
* and properly used at runtime
4+
*/
5+
6+
import { IterableClient } from "../../src/client/index.js";
7+
8+
describe("IterableClient", () => {
9+
describe("Constructor with baseUrl", () => {
10+
it("should use US endpoint when explicitly provided", () => {
11+
const client = new IterableClient({
12+
apiKey: "a1b2c3d4e5f6789012345678901234ab",
13+
baseUrl: "https://api.iterable.com",
14+
});
15+
16+
expect(client).toBeDefined();
17+
expect(client.client.defaults.baseURL).toBe("https://api.iterable.com");
18+
});
19+
20+
it("should use EU endpoint when explicitly provided", () => {
21+
const client = new IterableClient({
22+
apiKey: "a1b2c3d4e5f6789012345678901234ab",
23+
baseUrl: "https://api.eu.iterable.com",
24+
});
25+
26+
expect(client).toBeDefined();
27+
expect(client.client.defaults.baseURL).toBe(
28+
"https://api.eu.iterable.com"
29+
);
30+
});
31+
32+
it("should use custom endpoint when provided", () => {
33+
const client = new IterableClient({
34+
apiKey: "a1b2c3d4e5f6789012345678901234ab",
35+
baseUrl: "https://custom.api.example.com",
36+
});
37+
38+
expect(client).toBeDefined();
39+
expect(client.client.defaults.baseURL).toBe(
40+
"https://custom.api.example.com"
41+
);
42+
});
43+
44+
it("should accept all valid IterableConfig properties", () => {
45+
const client = new IterableClient({
46+
apiKey: "a1b2c3d4e5f6789012345678901234ab",
47+
baseUrl: "https://api.iterable.com",
48+
timeout: 45000,
49+
debug: false,
50+
debugVerbose: true,
51+
customHeaders: {
52+
"X-Custom-1": "value1",
53+
"X-Custom-2": "value2",
54+
},
55+
});
56+
57+
expect(client).toBeDefined();
58+
expect(client.client.defaults.baseURL).toBe("https://api.iterable.com");
59+
expect(client.client.defaults.timeout).toBe(45000);
60+
});
61+
});
62+
63+
describe("Constructor with Injected Client", () => {
64+
it("should use the injected axios instance ignoring baseUrl config", () => {
65+
const mockAxios = {
66+
defaults: {
67+
// note this is the axios instance's baseURL, not the IterableConfig's baseUrl
68+
baseURL: "https://mock.example.com",
69+
},
70+
interceptors: {
71+
request: { use: jest.fn() },
72+
response: { use: jest.fn() },
73+
},
74+
} as any;
75+
76+
const client = new IterableClient(
77+
{
78+
apiKey: "a1b2c3d4e5f6789012345678901234ab",
79+
baseUrl: "https://api.iterable.com",
80+
},
81+
mockAxios
82+
);
83+
84+
expect(client).toBeDefined();
85+
expect(client.client).toBe(mockAxios);
86+
// The injected client's baseURL should be preserved, not overridden
87+
expect(client.client.defaults.baseURL).toBe("https://mock.example.com");
88+
});
89+
});
90+
});

0 commit comments

Comments
 (0)