Skip to content

Commit

Permalink
accept a function as token (#1069)
Browse files Browse the repository at this point in the history
  • Loading branch information
tusharpandey13 authored Jan 10, 2025
1 parent fbe605c commit 4b99afd
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/management/management-client-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface ManagementClientOptions extends ClientOptions {
}

export interface ManagementClientOptionsWithToken extends ManagementClientOptions {
token: string;
token: string | (() => Promise<string>) | (() => string);
}

export interface ManagementClientOptionsWithClientSecret extends ManagementClientOptions {
Expand Down
6 changes: 4 additions & 2 deletions src/management/token-provider-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import {
} from './management-client-options.js';
import { TokenProvider } from './token-provider.js';

import { resolveValueToPromise } from '../utils.js';

export class TokenProviderMiddleware implements Middleware {
private tokenProvider: { getAccessToken: () => Promise<string> };
private readonly tokenProvider: { getAccessToken: () => Promise<string> };
constructor(
options: ManagementClientOptionsWithToken | ManagementClientOptionsWithClientCredentials
) {
if ('token' in options) {
this.tokenProvider = {
getAccessToken: () => Promise.resolve(options.token),
getAccessToken: () => resolveValueToPromise<string>(options.token),
};
} else {
this.tokenProvider = new TokenProvider({
Expand Down
22 changes: 22 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,25 @@ export const generateClientInfo = () => ({
* @private
*/
export const mtlsPrefix = 'mtls';

type SyncGetter<T> = () => T;
type AsyncGetter<T> = () => Promise<T>;
/**
* Resolves a value that can be a static value, a synchronous function, or an asynchronous function.
*
* @template T - The type of the value to be resolved.
* @param {T | SyncGetter<T> | AsyncGetter<T>} value - The value to be resolved. It can be:
* - A static value of type T.
* - A synchronous function that returns a value of type T.
* - An asynchronous function that returns a Promise of type T.
* @returns {Promise<T>} A promise that resolves to the value of type T.
*/
export const resolveValueToPromise = async <T>(
value: T | SyncGetter<T> | AsyncGetter<T>
): Promise<T> => {
if (typeof value === 'function') {
const result = (value as SyncGetter<T> | AsyncGetter<T>)(); // Call the function
return result instanceof Promise ? result : Promise.resolve(result); // Handle sync/async
}
return Promise.resolve(value); // Static value
};
23 changes: 23 additions & 0 deletions test/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolveValueToPromise } from '../../src/utils.js';

describe('resolveValueToPromise', () => {
it('should resolve a static string value', async () => {
const value = 'staticValue';
const result = await resolveValueToPromise(value);
expect(result).toBe(value);
});

it('should resolve a synchronous function returning a string', async () => {
const value = 'syncValue';
const syncFunction = () => value;
const result = await resolveValueToPromise(syncFunction);
expect(result).toBe(value);
});

it('should resolve an asynchronous function returning a string', async () => {
const value = 'asyncValue';
const asyncFunction = async () => value;
const result = await resolveValueToPromise(asyncFunction);
expect(result).toBe(value);
});
});
55 changes: 55 additions & 0 deletions test/management/token-provider-middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,59 @@ describe('TokenProviderMiddleware', () => {
).resolves.toMatchObject({});
expect(customFetch).toHaveBeenCalled();
});

it('should use provided access token as a string', async () => {
await expect(tokenClient.testRequest({ path: '/foo', method: 'GET' })).resolves.toMatchObject(
{}
);
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
authorization: 'Bearer token',
})
);
});

it('should use provided access token as a sync function', async () => {
const syncTokenClient = new TestClient({
...opts,
middleware: [
new TokenProviderMiddleware({
...opts,
domain,
token: () => 'sync-token',
}),
],
});

await expect(
syncTokenClient.testRequest({ path: '/foo', method: 'GET' })
).resolves.toMatchObject({});
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
authorization: 'Bearer sync-token',
})
);
});

it('should use provided access token as an async function', async () => {
const asyncTokenClient = new TestClient({
...opts,
middleware: [
new TokenProviderMiddleware({
...opts,
domain,
token: async () => 'async-token',
}),
],
});

await expect(
asyncTokenClient.testRequest({ path: '/foo', method: 'GET' })
).resolves.toMatchObject({});
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
authorization: 'Bearer async-token',
})
);
});
});

0 comments on commit 4b99afd

Please sign in to comment.