Skip to content

Commit ad61319

Browse files
committed
test: move fetch test to unit suite; enrich JSDoc and fix example
Follow-ups on top of the custom-fetch fix: - Move the wrapper test from tests/management/ into the unit suite (src/management/tests/unit/) alongside the other wrapper tests, and assert on the real rules.list() return value. - Add an explicit enriched JSDoc on the public `fetch` field. - Fix the adjacent class-level example to use `fetch:` instead of the `fetcher:` callback the constructor deletes.
1 parent bc924da commit ad61319

3 files changed

Lines changed: 73 additions & 59 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { ManagementClient } from "../../wrapper/ManagementClient.js";
2+
3+
describe("ManagementClient custom fetch option", () => {
4+
// Regression for https://github.com/auth0/node-auth0/issues/1330
5+
// The wrapper used to `delete (_options as any).fetch`, which silently
6+
// dropped any caller-provided custom fetch implementation even though the
7+
// underlying `fetcherImpl` honours `fetchFn`. These tests verify the
8+
// wrapper now forwards `fetch` all the way through to the transport.
9+
it("should route requests through the provided custom fetch implementation", async () => {
10+
const calls: { url: string; init?: RequestInit }[] = [];
11+
const customFetch: typeof fetch = (async (input, init) => {
12+
const url = typeof input === "string" ? input : (input as URL | Request).toString();
13+
calls.push({ url, init });
14+
return new Response(JSON.stringify({ rules: [{ id: "rul_test", name: "Test Rule", enabled: true }] }), {
15+
status: 200,
16+
headers: { "Content-Type": "application/json" },
17+
});
18+
}) as typeof fetch;
19+
20+
const client = new ManagementClient({
21+
domain: "tenant.auth0.com",
22+
token: "test-token",
23+
fetch: customFetch,
24+
});
25+
26+
const page = await client.rules.list();
27+
28+
// The custom fetch was actually used for the request...
29+
expect(calls.length).toBeGreaterThanOrEqual(1);
30+
expect(calls[0].url).toContain("tenant.auth0.com/api/v2/rules");
31+
// ...and the response it returned is what the client parsed and handed back.
32+
expect(page.data).toEqual([{ id: "rul_test", name: "Test Rule", enabled: true }]);
33+
});
34+
35+
it("should not throw when the `fetch` option is omitted (defaults to global fetch)", () => {
36+
expect(
37+
() =>
38+
new ManagementClient({
39+
domain: "tenant.auth0.com",
40+
token: "test-token",
41+
}),
42+
).not.toThrow();
43+
});
44+
});

src/management/wrapper/ManagementClient.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,29 @@ export declare namespace ManagementClient {
2222
* @group Management API
2323
* @public
2424
*/
25-
export interface ManagementClientOptions extends Omit<
26-
FernClient.Options,
27-
"token" | "environment" | "fetcher" | "baseUrl"
28-
> {
25+
export interface ManagementClientOptions
26+
extends Omit<FernClient.Options, "token" | "environment" | "fetcher" | "baseUrl"> {
2927
/** Auth0 domain (e.g., 'your-tenant.auth0.com') */
3028
domain: string;
29+
/**
30+
* Custom `fetch` implementation used for all HTTP requests.
31+
*
32+
* Provide your own transport when you need to control how requests are
33+
* made — for example to route through a proxy or corporate egress, add
34+
* retry middleware, attach OpenTelemetry instrumentation, or run on
35+
* platforms where the global `fetch` must be swapped (Cloudflare
36+
* Workers, Bun). Defaults to the platform's global `fetch`.
37+
*
38+
* @example
39+
* ```typescript
40+
* const client = new ManagementClient({
41+
* domain: 'your-tenant.auth0.com',
42+
* token: 'your-static-token',
43+
* fetch: myInstrumentedFetch,
44+
* });
45+
* ```
46+
*/
47+
fetch?: typeof fetch;
3148
/**
3249
* API audience. Defaults to https://{domain}/api/v2/
3350
* @defaultValue `https://{domain}/api/v2/`
@@ -182,16 +199,16 @@ export declare namespace ManagementClient {
182199
* });
183200
* ```
184201
*
185-
* @example Using custom fetcher with custom domain header (they work together)
202+
* @example Using a custom fetch with custom domain header (they work together)
186203
* ```typescript
187204
* const client = new ManagementClient({
188205
* domain: 'your-tenant.auth0.com',
189206
* clientId: 'your-client-id',
190207
* clientSecret: 'your-client-secret',
191208
* withCustomDomainHeader: 'auth.example.com', // Custom domain header logic
192-
* fetcher: async (args) => {
193-
* console.log('Making request:', args.url); // Custom logging
194-
* return fetch(args.url, { ...args }); // Custom fetch implementation
209+
* fetch: (input, init) => {
210+
* console.log('Making request:', input); // Custom logging
211+
* return fetch(input, init); // Custom fetch implementation
195212
* }
196213
* });
197214
* ```
@@ -208,11 +225,10 @@ export class ManagementClient extends FernClient {
208225
const headers = createTelemetryHeaders(_options);
209226
const token = createTokenSupplier(_options);
210227

211-
// The underlying fetcher type is internal to the generated Fern client
212-
// and is intentionally kept off the public surface. `fetch`, however,
213-
// is supported all the way down through `fetcherImpl` and is a
214-
// legitimate customization hook for callers that need to control the
215-
// HTTP transport (proxies, retries, instrumentation).
228+
// The underlying `fetcher` type is a Fern implementation detail and is
229+
// intentionally kept off the public surface, so strip it here. `fetch`,
230+
// by contrast, is supported all the way down through `fetcherImpl` and
231+
// is a legitimate transport-customization hook, so it is left in place.
216232
// https://github.com/auth0/node-auth0/issues/1330
217233
delete (_options as any).fetcher;
218234

tests/management/ManagementClient.test.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)