From 772a66edf1fdabc4c4c80087f534dd02de216da7 Mon Sep 17 00:00:00 2001 From: printy6 Date: Sun, 5 Jan 2025 20:20:42 +0800 Subject: [PATCH] feat(framework): add `fetchOptions` parameter to `Client` for custom fetch configuration close #7432 --- packages/framework/src/client.ts | 4 ++++ packages/framework/src/handler.ts | 2 +- .../framework/src/resources/workflow/workflow.resource.ts | 2 +- packages/framework/src/types/config.types.ts | 6 ++++++ packages/framework/src/types/event.types.ts | 4 ++++ packages/framework/src/utils/http.utils.ts | 8 +++++++- 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/client.ts b/packages/framework/src/client.ts index e760163670a..0cc106c892e 100644 --- a/packages/framework/src/client.ts +++ b/packages/framework/src/client.ts @@ -67,6 +67,8 @@ export class Client { public apiUrl: string; + public fetchOptions?: Record; + public version: string = SDK_VERSION; public strictAuthentication: boolean; @@ -75,6 +77,7 @@ export class Client { const builtOpts = this.buildOptions(options); this.apiUrl = builtOpts.apiUrl; this.secretKey = builtOpts.secretKey; + this.fetchOptions = builtOpts.fetchOptions; this.strictAuthentication = builtOpts.strictAuthentication; this.templateEngine.registerFilter('json', (value, spaces) => stringifyDataStructureWithSingleQuotes(value, spaces) @@ -85,6 +88,7 @@ export class Client { const builtConfiguration: Required = { apiUrl: resolveApiUrl(providedOptions?.apiUrl), secretKey: resolveSecretKey(providedOptions?.secretKey), + fetchOptions: providedOptions?.fetchOptions ?? {}, strictAuthentication: !isRuntimeInDevelopment(), }; diff --git a/packages/framework/src/handler.ts b/packages/framework/src/handler.ts index 90bec00c5c9..1f6d1449400 100644 --- a/packages/framework/src/handler.ts +++ b/packages/framework/src/handler.ts @@ -72,7 +72,7 @@ export class NovuRequestHandler { this.handler = options.handler; this.client = options.client ? options.client : new Client(); this.workflows = options.workflows; - this.http = initApiClient(this.client.secretKey, this.client.apiUrl); + this.http = initApiClient(this.client.secretKey, this.client.apiUrl, this.client.fetchOptions); this.frameworkName = options.frameworkName; this.hmacEnabled = this.client.strictAuthentication; } diff --git a/packages/framework/src/resources/workflow/workflow.resource.ts b/packages/framework/src/resources/workflow/workflow.resource.ts index 09bdc9e9453..62d70b8624a 100644 --- a/packages/framework/src/resources/workflow/workflow.resource.ts +++ b/packages/framework/src/resources/workflow/workflow.resource.ts @@ -36,7 +36,7 @@ export function workflow< const options = workflowOptions || {}; const trigger: Workflow['trigger'] = async (event) => { - const apiClient = initApiClient(resolveSecretKey(event.secretKey), resolveApiUrl(event.apiUrl)); + const apiClient = initApiClient(resolveSecretKey(event.secretKey), resolveApiUrl(event.apiUrl), event.fetchOptions); const unvalidatedData = (event.payload || {}) as T_PayloadUnvalidated; let validatedData: T_PayloadValidated; diff --git a/packages/framework/src/types/config.types.ts b/packages/framework/src/types/config.types.ts index 65f9c9e4cfe..c3123e20d68 100644 --- a/packages/framework/src/types/config.types.ts +++ b/packages/framework/src/types/config.types.ts @@ -23,4 +23,10 @@ export type ClientOptions = { * Defaults to true. */ strictAuthentication?: boolean; + + /** + * Additional fetch options to be passed to the fetch API. + * This can be used to set custom agent, headers, etc. + */ + fetchOptions?: Record; }; diff --git a/packages/framework/src/types/event.types.ts b/packages/framework/src/types/event.types.ts index 491b3d77fa1..9ebcbbe127b 100644 --- a/packages/framework/src/types/event.types.ts +++ b/packages/framework/src/types/event.types.ts @@ -59,6 +59,10 @@ export type EventTriggerParams = { * Override secret key for the trigger */ secretKey?: string; + /** + * Additional fetch options to be passed to the fetch API. + */ + fetchOptions?: Record; } & ConditionalPartial< { /** diff --git a/packages/framework/src/utils/http.utils.ts b/packages/framework/src/utils/http.utils.ts index d8e9e5a914c..909d9ad85c0 100644 --- a/packages/framework/src/utils/http.utils.ts +++ b/packages/framework/src/utils/http.utils.ts @@ -1,7 +1,9 @@ import { checkIsResponseError } from '../shared'; import { BridgeError, MissingSecretKeyError, PlatformError } from '../errors'; -export const initApiClient = (secretKey: string, apiUrl: string) => { +export const initApiClient = (secretKey: string, apiUrl: string, fetchOptions?: Record) => { + const { headers: additionalHeaders, ...restFetchOptions } = fetchOptions || {}; + if (!secretKey) { throw new MissingSecretKeyError(); } @@ -9,8 +11,10 @@ export const initApiClient = (secretKey: string, apiUrl: string) => { return { post: async (route: string, data: Record): Promise => { const response = await fetch(`${apiUrl}/v1${route}`, { + ...restFetchOptions, method: 'POST', headers: { + ...additionalHeaders, 'Content-Type': 'application/json', Authorization: `ApiKey ${secretKey}`, }, @@ -30,8 +34,10 @@ export const initApiClient = (secretKey: string, apiUrl: string) => { delete: async (route: string): Promise => { return ( await fetch(`${apiUrl}/v1${route}`, { + ...restFetchOptions, method: 'DELETE', headers: { + ...additionalHeaders, 'Content-Type': 'application/json', Authorization: `ApiKey ${secretKey}`, },