diff --git a/src/axios-api-versioning-interceptor.ts b/src/axios-api-versioning-interceptor.ts index 7b8d754..7f9c2a8 100644 --- a/src/axios-api-versioning-interceptor.ts +++ b/src/axios-api-versioning-interceptor.ts @@ -1,14 +1,15 @@ import { AxiosInstance } from 'axios'; -import { VersioningStrategy, IVersioningConfig, } from './types' -import { AxiosRequestConfigWithVersioning } from './types/axios'; +import { VersioningStrategy, IVersioningConfig, AxiosRequestConfigWithVersioning } from './types'; function replaceUrlPathWithVersion(url: string, apiVersion: string) { // the template name of the api version must be "apiVersion" return url.replace('{apiVersion}', apiVersion); } -function enhanceConfigByVersioningStrategy(requestConfig: AxiosRequestConfigWithVersioning, versioningConfig: IVersioningConfig): AxiosRequestConfigWithVersioning { - +function enhanceConfigByVersioningStrategy( + requestConfig: AxiosRequestConfigWithVersioning, + versioningConfig: IVersioningConfig +): AxiosRequestConfigWithVersioning { // we prioritize the apiVersion passed via the RequestConfig first // then use the initial versioningConfig last const apiVersion = requestConfig['apiVersion'] || versioningConfig['apiVersion']; @@ -19,13 +20,13 @@ function enhanceConfigByVersioningStrategy(requestConfig: AxiosRequestConfigWith if (versioningStrategy === VersioningStrategy.QueryString) { requestConfig.params = { ...requestConfig.params, - [versioningConfig.queryStringKeyName]: apiVersion + [versioningConfig.queryStringKeyName]: apiVersion, }; } if (versioningStrategy === VersioningStrategy.MediaType) { - const defaultAcceptHeader: string = requestConfig.headers.common["Accept"]; - const reqAcceptHeader: string | undefined = requestConfig.headers["Accept"] || undefined; + const defaultAcceptHeader: string = requestConfig.headers.common['Accept']; + const reqAcceptHeader: string | undefined = requestConfig.headers['Accept'] || undefined; // we prioritize an accept header passed in the RequestConfig but default to the // the common default accept header value axios provides @@ -35,19 +36,18 @@ function enhanceConfigByVersioningStrategy(requestConfig: AxiosRequestConfigWith const formattedAcceptHeader = versioningConfig.mediaTypeFormatter({ apiVersion, acceptHeader, - mediaTypeKeyName: versioningConfig.mediaTypeKeyName - }) + mediaTypeKeyName: versioningConfig.mediaTypeKeyName, + }); requestConfig.headers = { ...requestConfig.headers, - ["Accept"]: formattedAcceptHeader - } - } - else { + ['Accept']: formattedAcceptHeader, + }; + } else { requestConfig.headers = { ...requestConfig.headers, - ["Accept"]: acceptHeader + `;${versioningConfig.mediaTypeKeyName}=${apiVersion}` - } + ['Accept']: acceptHeader + `;${versioningConfig.mediaTypeKeyName}=${apiVersion}`, + }; } } diff --git a/src/axios-api-versioning.ts b/src/axios-api-versioning.ts index a0b9723..d5734d8 100644 --- a/src/axios-api-versioning.ts +++ b/src/axios-api-versioning.ts @@ -1,7 +1,7 @@ import axios, { AxiosInstance, AxiosStatic } from 'axios'; import { AxiosInstanceWithVersioning } from './types/axios'; -import { IWithVersioningConfig, IVersioningConfig } from './types' -import { injectApiVersioningInterceptor } from './axios-api-versioning-interceptor' +import { IWithVersioningConfig, IVersioningConfig } from './types'; +import { injectApiVersioningInterceptor } from './axios-api-versioning-interceptor'; import { defaultWithVersioningConfig } from './defaultConfig'; export function withVersioning(instance: AxiosInstance | AxiosStatic, config: IWithVersioningConfig) { @@ -15,4 +15,4 @@ export function withVersioning(instance: AxiosInstance | AxiosStatic, config: IW injectApiVersioningInterceptor(clonedInstance, versioningConfig); return clonedInstance as AxiosInstanceWithVersioning; -} \ No newline at end of file +} diff --git a/src/defaultConfig.ts b/src/defaultConfig.ts index 0034467..7415975 100644 --- a/src/defaultConfig.ts +++ b/src/defaultConfig.ts @@ -1,6 +1,6 @@ import { IVersioningConfig, PickPartial } from './types'; -export const defaultWithVersioningConfig: PickPartial = { +export const defaultWithVersioningConfig: PickPartial = { mediaTypeKeyName: 'v', queryStringKeyName: 'api-version', -} \ No newline at end of file +}; diff --git a/src/index.ts b/src/index.ts index cc0430e..4400645 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export { withVersioning } from './axios-api-versioning'; export { VersioningStrategy, IWithVersioningConfig, MediaTypeFormatterFn } from './types'; +export * from './types/axios'; diff --git a/src/types/axios/axios.types.ts b/src/types/axios/axios.types.ts index 7c89fc4..41ace1b 100644 --- a/src/types/axios/axios.types.ts +++ b/src/types/axios/axios.types.ts @@ -1,11 +1,6 @@ // Matching type definitions for axios 0.18 -import { - AxiosRequestConfig, - AxiosPromise, - AxiosInterceptorManager, - AxiosResponse, -} from 'axios'; +import { AxiosRequestConfig, AxiosPromise, AxiosInterceptorManager, AxiosResponse } from 'axios'; /** * In order to only expose the apiVersion and versioningStrategy to axios instances with @@ -44,15 +39,27 @@ export interface AxiosInstanceWithVersioning { get>(url: string, config?: AxiosRequestConfigWithVersioning): Promise; delete>(url: string, config?: AxiosRequestConfigWithVersioning): Promise; head>(url: string, config?: AxiosRequestConfigWithVersioning): Promise; - post>(url: string, data?: any, config?: AxiosRequestConfigWithVersioning): Promise; - put>(url: string, data?: any, config?: AxiosRequestConfigWithVersioning): Promise; - patch>(url: string, data?: any, config?: AxiosRequestConfigWithVersioning): Promise; + post>( + url: string, + data?: any, + config?: AxiosRequestConfigWithVersioning + ): Promise; + put>( + url: string, + data?: any, + config?: AxiosRequestConfigWithVersioning + ): Promise; + patch>( + url: string, + data?: any, + config?: AxiosRequestConfigWithVersioning + ): Promise; } export default interface AxiosTypes { - AxiosRequestConfigWithVersioning: AxiosRequestConfigWithVersioning, - AxiosAdapterWithVersioning: AxiosAdapterWithVersioning, - AxiosResponseWithVersioning: AxiosResponseWithVersioning, - AxiosErrorWithVersioning: AxiosErrorWithVersioning, - AxiosInstanceWithVersioning: AxiosInstanceWithVersioning, -} \ No newline at end of file + AxiosRequestConfigWithVersioning: AxiosRequestConfigWithVersioning; + AxiosAdapterWithVersioning: AxiosAdapterWithVersioning; + AxiosResponseWithVersioning: AxiosResponseWithVersioning; + AxiosErrorWithVersioning: AxiosErrorWithVersioning; + AxiosInstanceWithVersioning: AxiosInstanceWithVersioning; +} diff --git a/src/types/axios/index.ts b/src/types/axios/index.ts index bfe61e2..8d37b05 100644 --- a/src/types/axios/index.ts +++ b/src/types/axios/index.ts @@ -4,4 +4,4 @@ export { AxiosResponseWithVersioning, AxiosErrorWithVersioning, AxiosInstanceWithVersioning, -} from './axios.types' \ No newline at end of file +} from './axios.types'; diff --git a/src/types/index.ts b/src/types/index.ts index ba7b896..8183983 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,7 +1,3 @@ -export { - VersioningStrategy, - MediaTypeFormatterFn, - IVersioningConfig, - IWithVersioningConfig, - PickPartial, -} from './types' \ No newline at end of file +export { VersioningStrategy, MediaTypeFormatterFn, IVersioningConfig, IWithVersioningConfig, PickPartial } from './types'; + +export * from './axios'; diff --git a/src/types/types.ts b/src/types/types.ts index 00659f1..90d9c40 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,14 +1,10 @@ export enum VersioningStrategy { QueryString = 'QUERY_STRING', UrlPath = 'URL_PATH', - MediaType = 'MEDIA_TYPE' + MediaType = 'MEDIA_TYPE', } -export type MediaTypeFormatterFn = (data: { - apiVersion: string, - mediaTypeKeyName: string, - acceptHeader: string -}) => string; +export type MediaTypeFormatterFn = (data: { apiVersion: string; mediaTypeKeyName: string; acceptHeader: string }) => string; export interface IVersioningConfig { apiVersion: string; @@ -18,10 +14,8 @@ export interface IVersioningConfig { mediaTypeFormatter?: MediaTypeFormatterFn; } -export interface IWithVersioningConfig - extends PickPartial { -} +export interface IWithVersioningConfig extends PickPartial {} // type helper // @see https://stackoverflow.com/a/53742583 -export type PickPartial = Pick> & Partial> \ No newline at end of file +export type PickPartial = Pick> & Partial>; diff --git a/tests/axios-api-versioning.mock.ts b/tests/axios-api-versioning.mock.ts index 604877f..0102d28 100644 --- a/tests/axios-api-versioning.mock.ts +++ b/tests/axios-api-versioning.mock.ts @@ -1,79 +1,79 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import * as status from "http-status-codes"; -import { withVersioning } from "../axios-api-versioning"; -import { IWithVersioningConfig, VersioningStrategy } from "../types"; -import { AxiosInstanceWithVersioning } from "../types/axios"; - -const test_url = "http://localhost:3000"; -const MOCK_RES = "hello_world"; +import axios, { AxiosInstance } from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import * as status from 'http-status-codes'; +import { withVersioning } from '../src/axios-api-versioning'; +import { IWithVersioningConfig, VersioningStrategy } from '../src/types'; +import { AxiosInstanceWithVersioning } from '../src/types/axios'; + +const test_url = 'http://localhost:3000'; +const MOCK_RES = 'hello_world'; let mock: MockAdapter; let instance: AxiosInstanceWithVersioning; describe('Testing correct response config of "QueryString" strategy', () => { - let versioningConfig: IWithVersioningConfig = { - apiVersion: "1.0", - versioningStrategy: VersioningStrategy.QueryString - }; + let versioningConfig: IWithVersioningConfig = { + apiVersion: '1.0', + versioningStrategy: VersioningStrategy.QueryString, + }; - beforeAll(() => { - instance = withVersioning(axios, versioningConfig); - mock = new MockAdapter(instance); - }); + beforeAll(() => { + instance = withVersioning(axios, versioningConfig); + mock = new MockAdapter(instance as AxiosInstance); + }); - test('it should have the "apiVersion" as a query param in the response config', async () => { - mock.onGet(test_url).reply(status.OK, MOCK_RES); + test('it should have the "apiVersion" as a query param in the response config', async () => { + mock.onGet(test_url).reply(status.OK, MOCK_RES); - const res = await instance.get(test_url); - const { params } = res.config; + const res = await instance.get(test_url); + const { params } = res.config; - expect(params).toHaveProperty("api-version"); - expect(params["api-version"]).toBe(versioningConfig.apiVersion); - }); + expect(params).toHaveProperty('api-version'); + expect(params['api-version']).toBe(versioningConfig.apiVersion); + }); }); describe('Testing correct response config of "MediaType" strategy', () => { - let versioningConfig: IWithVersioningConfig = { - apiVersion: "1.0", - versioningStrategy: VersioningStrategy.MediaType - }; + let versioningConfig: IWithVersioningConfig = { + apiVersion: '1.0', + versioningStrategy: VersioningStrategy.MediaType, + }; - beforeAll(() => { - instance = withVersioning(axios, versioningConfig); - mock = new MockAdapter(instance); - }); + beforeAll(() => { + instance = withVersioning(axios, versioningConfig); + mock = new MockAdapter(instance as AxiosInstance); + }); - test('it should have the "apiVersion" as an accept-param in the Accept header', async () => { - mock.onGet(test_url).reply(status.OK, MOCK_RES); + test('it should have the "apiVersion" as an accept-param in the Accept header', async () => { + mock.onGet(test_url).reply(status.OK, MOCK_RES); - const res = await instance.get(test_url); - const { headers } = res.config; + const res = await instance.get(test_url); + const { headers } = res.config; - expect(headers).toHaveProperty("Accept"); - expect(headers["Accept"]).toMatch(`v=${versioningConfig.apiVersion}`); - }); + expect(headers).toHaveProperty('Accept'); + expect(headers['Accept']).toMatch(`v=${versioningConfig.apiVersion}`); + }); }); describe('Testing correct response config of "UrlPath" strategy', () => { - let versioningConfig: IWithVersioningConfig = { - apiVersion: "1", - versioningStrategy: VersioningStrategy.UrlPath - }; + let versioningConfig: IWithVersioningConfig = { + apiVersion: '1', + versioningStrategy: VersioningStrategy.UrlPath, + }; - const blank_test_url = test_url + "/v{apiVersion}"; - const versioned_test_url = test_url + `/v${versioningConfig.apiVersion}`; + const blank_test_url = test_url + '/v{apiVersion}'; + const versioned_test_url = test_url + `/v${versioningConfig.apiVersion}`; - beforeAll(() => { - instance = withVersioning(axios, versioningConfig); - mock = new MockAdapter(instance); - }); + beforeAll(() => { + instance = withVersioning(axios, versioningConfig); + mock = new MockAdapter(instance as AxiosInstance); + }); - test('it should have the "apiVersion" as a url param in the url', async () => { - mock.onGet(versioned_test_url).reply(status.OK, MOCK_RES); + test('it should have the "apiVersion" as a url param in the url', async () => { + mock.onGet(versioned_test_url).reply(status.OK, MOCK_RES); - const res = await instance.get(blank_test_url); - const { url } = res.config; + const res = await instance.get(blank_test_url); + const { url } = res.config; - expect(url).toBe(versioned_test_url); - }); + expect(url).toBe(versioned_test_url); + }); }); diff --git a/tests/axios-api-versioning.spec.ts b/tests/axios-api-versioning.spec.ts index 2444d17..202482c 100644 --- a/tests/axios-api-versioning.spec.ts +++ b/tests/axios-api-versioning.spec.ts @@ -1,81 +1,81 @@ -import axios, { AxiosInstance } from "axios"; -import { withVersioning } from "../axios-api-versioning"; -import { IWithVersioningConfig, VersioningStrategy } from "../types"; -import { AxiosInstanceWithVersioning } from "../types/axios"; +import axios, { AxiosInstance } from 'axios'; +import { withVersioning } from '../src/axios-api-versioning'; +import { IWithVersioningConfig, VersioningStrategy } from '../src/types'; +import { AxiosInstanceWithVersioning } from '../src/types/axios'; const testVersioningConfig: IWithVersioningConfig = { - apiVersion: "1.0", - versioningStrategy: VersioningStrategy.QueryString + apiVersion: '1.0', + versioningStrategy: VersioningStrategy.QueryString, }; // define an index type to stop TS from complaining about // accessing key/values with an index lookup -declare module "axios" { - interface AxiosRequestConfig { - [key: string]: any; - } +declare module 'axios' { + interface AxiosRequestConfig { + [key: string]: any; + } } -describe("Testing no pollution on default exported axios object using withVersioning()", () => { - beforeAll(() => { - const instance = withVersioning(axios, testVersioningConfig); - }); +describe('Testing no pollution on default exported axios object using withVersioning()', () => { + beforeAll(() => { + const instance = withVersioning(axios, testVersioningConfig); + }); - test('it should not add "apiVersion" to AxiosStatic defaults', () => { - expect(axios.defaults["apiVersion"]).toBe(undefined); - }); + test('it should not add "apiVersion" to AxiosStatic defaults', () => { + expect(axios.defaults['apiVersion']).toBe(undefined); + }); - test('it should not add "versioningStrategy" to AxiosStatic defaults', () => { - expect(axios.defaults["versioningStrategy"]).toBe(undefined); - }); + test('it should not add "versioningStrategy" to AxiosStatic defaults', () => { + expect(axios.defaults['versioningStrategy']).toBe(undefined); + }); - test('it should not add "ApiVersioningInterceptor" to AxiosStatic interceptors', () => { - // @ts-ignore - // we directly check the length of the handlers array for the request interceptors - expect(axios.interceptors.request["handlers"].length).toBe(0); - }); + test('it should not add "ApiVersioningInterceptor" to AxiosStatic interceptors', () => { + // @ts-ignore + // we directly check the length of the handlers array for the request interceptors + expect(axios.interceptors.request['handlers'].length).toBe(0); + }); }); -describe("Testing correct return value of withVersioning()", () => { - let instance: AxiosInstanceWithVersioning; +describe('Testing correct return value of withVersioning()', () => { + let instance: AxiosInstanceWithVersioning; - beforeAll(() => { - instance = withVersioning(axios, testVersioningConfig); - }); + beforeAll(() => { + instance = withVersioning(axios, testVersioningConfig); + }); - test('it should correctly add "ApiVersioningInterceptor" to instance interceptors', () => { - // @ts-ignore - // we directly check the length of the handlers array for the request interceptors - expect(instance.interceptors.request["handlers"].length).toBe(1); - }); + test('it should correctly add "ApiVersioningInterceptor" to instance interceptors', () => { + // @ts-ignore + // we directly check the length of the handlers array for the request interceptors + expect(instance.interceptors.request['handlers'].length).toBe(1); + }); }); -describe("Testing correct return value of withVersioning() using axios.create()", () => { - let axiosInstance: AxiosInstance; - let instance: AxiosInstanceWithVersioning; +describe('Testing correct return value of withVersioning() using axios.create()', () => { + let axiosInstance: AxiosInstance; + let instance: AxiosInstanceWithVersioning; - beforeAll(() => { - axiosInstance = axios.create(); - instance = withVersioning(axiosInstance, testVersioningConfig); - }); + beforeAll(() => { + axiosInstance = axios.create(); + instance = withVersioning(axiosInstance, testVersioningConfig); + }); - test('it should correctly add "ApiVersioningInterceptor" to instance interceptors', () => { - // @ts-ignore - // we directly check the length of the handlers array for the request interceptors - expect(instance.interceptors.request["handlers"].length).toBe(1); - }); + test('it should correctly add "ApiVersioningInterceptor" to instance interceptors', () => { + // @ts-ignore + // we directly check the length of the handlers array for the request interceptors + expect(instance.interceptors.request['handlers'].length).toBe(1); + }); - test('it should not add "apiVersion" to axios.create() instance', () => { - expect(axiosInstance.defaults["apiVersion"]).toBe(undefined); - }); + test('it should not add "apiVersion" to axios.create() instance', () => { + expect(axiosInstance.defaults['apiVersion']).toBe(undefined); + }); - test('it should not add "versioningStrategy" to axios.create() instance', () => { - expect(axiosInstance.defaults["versioningStrategy"]).toBe(undefined); - }); + test('it should not add "versioningStrategy" to axios.create() instance', () => { + expect(axiosInstance.defaults['versioningStrategy']).toBe(undefined); + }); - test('it should not add "ApiVersioningInterceptor" to axios.create() instance', () => { - // @ts-ignore - // we directly check the length of the handlers array for the request interceptors - expect(axiosInstance.interceptors.request["handlers"].length).toBe(0); - }); + test('it should not add "ApiVersioningInterceptor" to axios.create() instance', () => { + // @ts-ignore + // we directly check the length of the handlers array for the request interceptors + expect(axiosInstance.interceptors.request['handlers'].length).toBe(0); + }); }); diff --git a/tsconfig.json b/tsconfig.json index d7e223b..2fb450b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,30 +1,30 @@ { - "include": ["src", "types"], - "compilerOptions": { - "target": "es5", - "module": "esnext", - "lib": ["dom", "esnext"], - "importHelpers": true, - "declaration": true, - "sourceMap": true, - "rootDir": "./", - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "*": ["src/*", "node_modules/*"] - }, - "jsx": "react", - "esModuleInterop": true - } + "include": ["src", "types"], + "compilerOptions": { + "target": "es5", + "module": "esnext", + "lib": ["dom", "esnext"], + "importHelpers": true, + "declaration": true, + "sourceMap": true, + "rootDir": "./", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "baseUrl": "./", + "paths": { + "*": ["src/*", "node_modules/*"] + }, + "jsx": "react", + "esModuleInterop": true + } }