From 5aa222c175a0d51f2571dd08cbfc87bdd4eceb94 Mon Sep 17 00:00:00 2001 From: Denwa Date: Mon, 9 Jun 2025 15:18:54 +0300 Subject: [PATCH] feat: Add types exports --- README.md | 3 +- src/index.ts | 273 +------------------------------------------ src/openapi-axios.ts | 266 +++++++++++++++++++++++++++++++++++++++++ src/types/index.ts | 6 + 4 files changed, 277 insertions(+), 271 deletions(-) create mode 100644 src/openapi-axios.ts create mode 100644 src/types/index.ts diff --git a/README.md b/README.md index 415d127..a863db7 100644 --- a/README.md +++ b/README.md @@ -135,4 +135,5 @@ Special thanks to - [@IRaccoonI](https://github.com/IRaccoonI) (contributor) - [@Denwa799](https://github.com/Denwa799) (contributor) -- [@simplesmiler](https://github.com/simplesmiler) (creator of [taxios](https://github.com/simplesmiler/taxios)) +- [@simplesmiler](https://github.com/simplesmiler) (creator of + [taxios](https://github.com/simplesmiler/taxios)) diff --git a/src/index.ts b/src/index.ts index 208a62a..8bdb2b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,270 +1,3 @@ -import type { - AxiosInstance, - AxiosRequestConfig, - AxiosResponse, - CustomParamsSerializer, -} from "axios"; -import { defaultOptions } from "./const/defaultOptions.js"; -import { type MethodType, type MethodTypeWithBody } from "./const/methods.js"; -import { interpolateParams } from "./interpolate-params.js"; -import type { - IOpenApiAxiosOptions, - OptionsType, - ValidStatusType, -} from "./types/options.js"; -import type { - FetcherWithBodyParameters, - FetcherWithoutBodyParameters, -} from "./types/requestParameters.js"; -import type { GetApiResponse } from "./types/response.js"; -import type { - RouteResponsesByStatusCode, - RoutesForMethod, - SchemaType, -} from "./types/schemeTypes.js"; -import type { IsNullable } from "./types/utils.js"; -import { getQuerySerializer } from "./utils/querySerializer.js"; -import { convertToAll } from "./utils/response-converters/convertToAll.js"; -import { convertToAxios } from "./utils/response-converters/convertToAxios.js"; -import { convertToFetch } from "./utils/response-converters/convertToFetch.js"; - -/** - * @description OpenApiAxios class is a wrapper around Axios that provides methods to handle API requests - * and responses based on OpenAPI specifications. - * - * @template Schema - The OpenAPI schema type. - * @template ClassValidStatus - The type that configures the error handling strategy. - */ -export class OpenApiAxios< - Schema extends SchemaType, - ClassValidStatus extends ValidStatusType, -> { - private axios: AxiosInstance; - private opt: IOpenApiAxiosOptions; - - /** - * @description Creates an instance of OpenApiAxios. - * - * @param axios - The Axios instance to use for requests. - * @param options - Configuration options for the OpenApiAxios instance. - */ - constructor( - axios: AxiosInstance, - options: IOpenApiAxiosOptions, - ) { - this.axios = axios; - this.opt = Object.assign({}, defaultOptions, options); - } - - // HTTP methods without a request body - public get = this.factoryWithoutBody("get"); - public head = this.factoryWithoutBody("head"); - public delete = this.factoryWithoutBody("delete"); - public options = this.factoryWithoutBody("options"); - - // HTTP methods with a request body - public put = this.factoryWithBody("put"); - public post = this.factoryWithBody("post"); - public patch = this.factoryWithBody("patch"); - - /** - * @description Generates a URI for a given API endpoint, including query parameters. - * - * @param method - The HTTP method to use. - * @param path - The API route. - * @param options - Additional options for the request. - * @returns The generated URI as a string. - */ - public async getUri< - Method extends MethodType, - Route extends RoutesForMethod, - >(method: Method, path: Route, options?: OptionsType) { - const { urlString, newOptions } = this.prepareOptions(path, options); - const { paramsSerializer, params, ...axios } = - this.optionsToAxiosOptions(newOptions); - - // Axios getUri doesn't support serializers - const queryUri = - Object.keys(params || {}).length > 0 - ? `?${(paramsSerializer as CustomParamsSerializer)(params)}` - : ""; - - return this.axios.getUri({ - url: `${urlString}${queryUri}`, - method, - ...axios, - }) as string; - } - - /** - * @description Prepares the options and URL for an API request. - * - * @param path - The API route. - * @param options - Additional options for the request. - * @returns An object containing the prepared URL string and new options. - */ - private prepareOptions< - Method extends MethodType, - Route extends RoutesForMethod, - MethodValidStatus extends ValidStatusType | undefined, - >( - path: Route, - options?: OptionsType, - ) { - let urlString = path as string; - - // Assign ValidStatus from class if not provided in request options - const newOptions: OptionsType< - Schema, - Method, - Route, - IsNullable extends true - ? ClassValidStatus - : NonNullable - > = Object.assign({}, options, { - validStatus: this.opt.validStatus, - }); - - // Create URL by interpolating parameters based on the OpenAPI pattern - // @ts-expect-error; @TODO See issue #2 - https://github.com/web-bee-ru/openapi-axios/issues/2 - if (newOptions?.params) - urlString = interpolateParams( - urlString, - // @ts-expect-error; @TODO See issue #2 - https://github.com/web-bee-ru/openapi-axios/issues/2 - newOptions.params, - ); - - // Assign query serializer to Axios - const querySerializationParams = - options?.querySerializationParams || this.opt.querySerializationParams; - if (!options?.axios?.paramsSerializer && querySerializationParams) { - newOptions.axios = Object.assign({}, newOptions.axios, { - paramsSerializer: getQuerySerializer(querySerializationParams), - }); - } - - return { - urlString, - newOptions, - }; - } - - /** - * @description Prepares the API response based on the valid status type. - * - * @param response - The Axios response promise. - * @param options - Options for the request. - * @returns A promise that resolves to the processed API response. - */ - private async prepareResponse< - ValidStatus extends ValidStatusType, - Method extends MethodType, - Route extends RoutesForMethod, - DataByCode extends Record = RouteResponsesByStatusCode< - Schema, - Method, - Route - >, - >( - response: Promise, - options: OptionsType, - ): Promise> { - switch (options.validStatus) { - case "all": - return convertToAll( - response, - ) as Promise>; - case "fetch": - return convertToFetch( - response, - ) as Promise>; - case "axios": - default: - return convertToAxios( - response, - ) as Promise>; - } - } - - /** - * @description Converts OpenAPI options to Axios request options. - * - * @param options - The options to convert. - * @returns The converted Axios request configuration. - */ - private optionsToAxiosOptions< - Method extends MethodType, - Route extends RoutesForMethod, - MethodValidStatus extends ValidStatusType, - >( - options: OptionsType, - ): AxiosRequestConfig { - return { - // @ts-expect-error; @TODO See issue #2 - https://github.com/web-bee-ru/openapi-axios/issues/2 - params: options?.query, - ...options.axios, - }; - } - - /** - * @description Creates a fetcher method for HTTP methods that do not have a request body (e.g., GET, DELETE). - * - * @param method - The HTTP method to create a fetcher for. - * @returns A function that performs the API request. - */ - private factoryWithoutBody< - Method extends Exclude, - >(method: Method) { - return < - Route extends RoutesForMethod, - MethodValidStatus extends ValidStatusType | undefined = undefined, - >( - ...args: FetcherWithoutBodyParameters< - Schema, - Method, - Route, - MethodValidStatus - > - ) => { - const [path, options] = args; - const { urlString, newOptions } = this.prepareOptions(path, options); - - return this.prepareResponse( - this.axios[method](urlString, this.optionsToAxiosOptions(newOptions)), - newOptions, - ); - }; - } - - /** - * @description Creates a fetcher method for HTTP methods that have a request body (e.g., POST, PUT). - * - * @param method - The HTTP method to create a fetcher for. - * @returns A function that performs the API request. - */ - private factoryWithBody(method: Method) { - return < - Route extends RoutesForMethod, - MethodValidStatus extends ValidStatusType | undefined, - >( - ...args: FetcherWithBodyParameters< - Schema, - Method, - Route, - MethodValidStatus - > - ) => { - const [path, body, options] = args; - const { urlString, newOptions } = this.prepareOptions(path, options); - - return this.prepareResponse( - this.axios[method]( - urlString, - body, - this.optionsToAxiosOptions(newOptions), - ), - newOptions, - ); - }; - } -} +export * from "./openapi-axios.js"; +export * from "./types/schemeTypes.js"; +export * from "./types/utils.js"; diff --git a/src/openapi-axios.ts b/src/openapi-axios.ts new file mode 100644 index 0000000..7fd0203 --- /dev/null +++ b/src/openapi-axios.ts @@ -0,0 +1,266 @@ +import type { + AxiosInstance, + AxiosRequestConfig, + AxiosResponse, + CustomParamsSerializer, +} from "axios"; +import { defaultOptions } from "./const/defaultOptions.js"; +import { type MethodType, type MethodTypeWithBody } from "./const/methods.js"; +import { interpolateParams } from "./interpolate-params.js"; +import type { + FetcherWithBodyParameters, + FetcherWithoutBodyParameters, + GetApiResponse, + IOpenApiAxiosOptions, + OptionsType, + RouteResponsesByStatusCode, + RoutesForMethod, + SchemaType, + ValidStatusType, +} from "./types/index.js"; +import type { IsNullable } from "./types/utils.js"; +import { getQuerySerializer } from "./utils/querySerializer.js"; +import { convertToAll } from "./utils/response-converters/convertToAll.js"; +import { convertToAxios } from "./utils/response-converters/convertToAxios.js"; +import { convertToFetch } from "./utils/response-converters/convertToFetch.js"; + +/** + * @description OpenApiAxios class is a wrapper around Axios that provides methods to handle API requests + * and responses based on OpenAPI specifications. + * + * @template Schema - The OpenAPI schema type. + * @template ClassValidStatus - The type that configures the error handling strategy. + */ +export class OpenApiAxios< + Schema extends SchemaType, + ClassValidStatus extends ValidStatusType, +> { + private axios: AxiosInstance; + private opt: IOpenApiAxiosOptions; + + /** + * @description Creates an instance of OpenApiAxios. + * + * @param axios - The Axios instance to use for requests. + * @param options - Configuration options for the OpenApiAxios instance. + */ + constructor( + axios: AxiosInstance, + options: IOpenApiAxiosOptions, + ) { + this.axios = axios; + this.opt = Object.assign({}, defaultOptions, options); + } + + // HTTP methods without a request body + public get = this.factoryWithoutBody("get"); + public head = this.factoryWithoutBody("head"); + public delete = this.factoryWithoutBody("delete"); + public options = this.factoryWithoutBody("options"); + + // HTTP methods with a request body + public put = this.factoryWithBody("put"); + public post = this.factoryWithBody("post"); + public patch = this.factoryWithBody("patch"); + + /** + * @description Generates a URI for a given API endpoint, including query parameters. + * + * @param method - The HTTP method to use. + * @param path - The API route. + * @param options - Additional options for the request. + * @returns The generated URI as a string. + */ + public async getUri< + Method extends MethodType, + Route extends RoutesForMethod, + >(method: Method, path: Route, options?: OptionsType) { + const { urlString, newOptions } = this.prepareOptions(path, options); + const { paramsSerializer, params, ...axios } = + this.optionsToAxiosOptions(newOptions); + + // Axios getUri doesn't support serializers + const queryUri = + Object.keys(params || {}).length > 0 + ? `?${(paramsSerializer as CustomParamsSerializer)(params)}` + : ""; + + return this.axios.getUri({ + url: `${urlString}${queryUri}`, + method, + ...axios, + }) as string; + } + + /** + * @description Prepares the options and URL for an API request. + * + * @param path - The API route. + * @param options - Additional options for the request. + * @returns An object containing the prepared URL string and new options. + */ + private prepareOptions< + Method extends MethodType, + Route extends RoutesForMethod, + MethodValidStatus extends ValidStatusType | undefined, + >( + path: Route, + options?: OptionsType, + ) { + let urlString = path as string; + + // Assign ValidStatus from class if not provided in request options + const newOptions: OptionsType< + Schema, + Method, + Route, + IsNullable extends true + ? ClassValidStatus + : NonNullable + > = Object.assign({}, options, { + validStatus: this.opt.validStatus, + }); + + // Create URL by interpolating parameters based on the OpenAPI pattern + // @ts-expect-error; @TODO See issue #2 - https://github.com/web-bee-ru/openapi-axios/issues/2 + if (newOptions?.params) + urlString = interpolateParams( + urlString, + // @ts-expect-error; @TODO See issue #2 - https://github.com/web-bee-ru/openapi-axios/issues/2 + newOptions.params, + ); + + // Assign query serializer to Axios + const querySerializationParams = + options?.querySerializationParams || this.opt.querySerializationParams; + if (!options?.axios?.paramsSerializer && querySerializationParams) { + newOptions.axios = Object.assign({}, newOptions.axios, { + paramsSerializer: getQuerySerializer(querySerializationParams), + }); + } + + return { + urlString, + newOptions, + }; + } + + /** + * @description Prepares the API response based on the valid status type. + * + * @param response - The Axios response promise. + * @param options - Options for the request. + * @returns A promise that resolves to the processed API response. + */ + private async prepareResponse< + ValidStatus extends ValidStatusType, + Method extends MethodType, + Route extends RoutesForMethod, + DataByCode extends Record = RouteResponsesByStatusCode< + Schema, + Method, + Route + >, + >( + response: Promise, + options: OptionsType, + ): Promise> { + switch (options.validStatus) { + case "all": + return convertToAll( + response, + ) as Promise>; + case "fetch": + return convertToFetch( + response, + ) as Promise>; + case "axios": + default: + return convertToAxios( + response, + ) as Promise>; + } + } + + /** + * @description Converts OpenAPI options to Axios request options. + * + * @param options - The options to convert. + * @returns The converted Axios request configuration. + */ + private optionsToAxiosOptions< + Method extends MethodType, + Route extends RoutesForMethod, + MethodValidStatus extends ValidStatusType, + >( + options: OptionsType, + ): AxiosRequestConfig { + return { + // @ts-expect-error; @TODO See issue #2 - https://github.com/web-bee-ru/openapi-axios/issues/2 + params: options?.query, + ...options.axios, + }; + } + + /** + * @description Creates a fetcher method for HTTP methods that do not have a request body (e.g., GET, DELETE). + * + * @param method - The HTTP method to create a fetcher for. + * @returns A function that performs the API request. + */ + private factoryWithoutBody< + Method extends Exclude, + >(method: Method) { + return < + Route extends RoutesForMethod, + MethodValidStatus extends ValidStatusType | undefined = undefined, + >( + ...args: FetcherWithoutBodyParameters< + Schema, + Method, + Route, + MethodValidStatus + > + ) => { + const [path, options] = args; + const { urlString, newOptions } = this.prepareOptions(path, options); + + return this.prepareResponse( + this.axios[method](urlString, this.optionsToAxiosOptions(newOptions)), + newOptions, + ); + }; + } + + /** + * @description Creates a fetcher method for HTTP methods that have a request body (e.g., POST, PUT). + * + * @param method - The HTTP method to create a fetcher for. + * @returns A function that performs the API request. + */ + private factoryWithBody(method: Method) { + return < + Route extends RoutesForMethod, + MethodValidStatus extends ValidStatusType | undefined, + >( + ...args: FetcherWithBodyParameters< + Schema, + Method, + Route, + MethodValidStatus + > + ) => { + const [path, body, options] = args; + const { urlString, newOptions } = this.prepareOptions(path, options); + + return this.prepareResponse( + this.axios[method]( + urlString, + body, + this.optionsToAxiosOptions(newOptions), + ), + newOptions, + ); + }; + } +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..780206c --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,6 @@ +export * from "./options.js"; +export * from "./requestParameters.js"; +export * from "./response.js"; +export * from "./schemeTypes.js"; +export * from "./serializer.js"; +export * from "./utils.js";