diff --git a/packages/commons/src/core/ApiClientBase.test.ts b/packages/commons/src/core/ApiClientBase.test.ts new file mode 100644 index 00000000..1fda986d --- /dev/null +++ b/packages/commons/src/core/ApiClientBase.test.ts @@ -0,0 +1,44 @@ +import axios from "axios"; +import { jest } from "@jest/globals"; +import ApiClientBase from "./ApiClientBase.js"; + +const requestFn = jest.fn(); + +jest.mock("axios"); +const mockedAxios = axios as jest.Mocked; +mockedAxios.create.mockReturnValue(mockedAxios); +mockedAxios.request.mockImplementation(requestFn as never); + +class TestClient extends ApiClientBase { + public testRequest = this.requestFunctionFactory({ + path: "/test", + method: "GET", + operationId: "test", + }); +} +const testClient = new TestClient(); + +test("onBeforeRequest is called before actual request", async () => { + const onBeforeRequest = jest.fn(() => { + expect(requestFn).not.toHaveBeenCalled(); + }); + + await testClient.testRequest(undefined, { + onBeforeRequest, + }); + + expect(onBeforeRequest).toHaveBeenCalledTimes(1); + expect(requestFn).toHaveBeenCalledTimes(1); +}); + +test("onBeforeRequest configured in default options is called before actual request", async () => { + const onBeforeRequest = jest.fn(() => { + expect(requestFn).not.toHaveBeenCalled(); + }); + + testClient.defaultRequestOptions.onBeforeRequest = onBeforeRequest; + await testClient.testRequest(undefined); + + expect(onBeforeRequest).toHaveBeenCalledTimes(1); + expect(requestFn).toHaveBeenCalledTimes(1); +}); diff --git a/packages/commons/src/core/ApiClientBase.ts b/packages/commons/src/core/ApiClientBase.ts index cd24ed4e..e37c2c56 100644 --- a/packages/commons/src/core/ApiClientBase.ts +++ b/packages/commons/src/core/ApiClientBase.ts @@ -1,21 +1,42 @@ import axios, { Axios, AxiosInstance, CreateAxiosDefaults } from "axios"; -import { RequestObject, RequestFunction } from "../types/index.js"; +import { + RequestObject, + RequestFunction, + RequestOptions, +} from "../types/index.js"; import { OpenAPIOperation } from "../types/index.js"; import Request from "./Request.js"; export abstract class ApiClientBase { - public axios: AxiosInstance; + public readonly axios: AxiosInstance; + public readonly defaultRequestOptions: RequestOptions = {}; public constructor(axiosConfig: AxiosInstance | CreateAxiosDefaults = axios) { this.axios = axiosConfig instanceof Axios ? axiosConfig : axios.create(axiosConfig); } + private buildRequestOptions( + fromRequest: RequestOptions, + ): RequestOptions { + return { + ...this.defaultRequestOptions, + ...fromRequest, + }; + } + protected requestFunctionFactory( operation: TOp, ): RequestFunction { - return (conf?: RequestObject) => - new Request(operation, conf).execute(this.axios); + return (conf?: RequestObject, opts: RequestOptions = {}) => { + const { onBeforeRequest } = this.buildRequestOptions(opts); + + const request = new Request(operation, conf); + if (onBeforeRequest) { + onBeforeRequest(request); + } + return request.execute(this.axios); + }; } } diff --git a/packages/commons/src/types/RequestFunction.ts b/packages/commons/src/types/RequestFunction.ts index 4bbff2a9..f1231924 100644 --- a/packages/commons/src/types/RequestFunction.ts +++ b/packages/commons/src/types/RequestFunction.ts @@ -4,6 +4,13 @@ import { OpenAPIOperation, } from "./OpenAPIOperation.js"; import { NullableOnNoRequiredKeysDeep } from "./NullableOnNoRequiredKeysDeep.js"; +import Request from "../core/Request.js"; + +export interface RequestOptions< + TOp extends OpenAPIOperation = OpenAPIOperation, +> { + onBeforeRequest?: (req: Request) => void; +} type UnboxPathParameters = T extends { pathParameters: infer TPath } ? Omit & TPath @@ -18,10 +25,12 @@ export type ResponsePromise = Promise< type RequestFunctionWithOptionalRequest = ( request?: RequestObject, + opts?: RequestOptions, ) => ResponsePromise; type RequestFunctionWithRequiredRequest = ( request: RequestObject, + opts?: RequestOptions, ) => ResponsePromise; export type RequestFunction =