UNPKG

@rocketmakers/api-swr

Version:

Rocketmakers front-end library for parsing a generated Typescript API client into a set of configurable React hooks for fetching and mutating data.

128 lines (127 loc) 8.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.axiosOpenApiControllerFactory = void 0; const tslib_1 = require("tslib"); const React = tslib_1.__importStar(require("react")); const useClientFetch_1 = require("../hooks/useClientFetch"); const useQuery_1 = require("../hooks/useQuery"); const api_1 = require("../utils/api"); const caching_1 = require("../utils/caching"); const useInfiniteQuery_1 = require("../hooks/useInfiniteQuery"); /** * Creates a factory of state management tools from an OpenAPI controller using Axios. * * @param {IAxiosOpenApiControllerSetup} options - An object containing options for the factory. * @param {string} options.basePath - The base URL path for the OpenAPI controller. * @param {Configuration} options.openApiConfig - The configuration object for OpenAPI HTTP requests. * @param {boolean} options.enableMocking - Will use mock endpoint definitions instead of calling out to the real API. * @param {APIProcessingHook} options.useApiProcessing - Optional processing hook for all client side fetches. * @param {GlobalFetchWrapperHook<TConfig>} options.useGlobalFetchWrapper - Optional fetch wrapper hook for all client side fetches. * @param {SWRConfiguration<any | undefined>} options.swrConfig - Additional config to send to SWR for all queries. * @param {SWRInfiniteConfiguration<any | undefined>} options.swrInfiniteConfig - Additional config to send to SWR for all infinite loader queries. * @returns {IAxiosOpenApiControllerFactory} A library of controller factory methods that create state management tools for an OpenAPI controller. */ const axiosOpenApiControllerFactory = ({ basePath, openApiConfig, enableMocking, useApiProcessing, useGlobalFetchWrapper, swrConfig, swrInfiniteConfig, }) => { /** * Creates a set of state management tools from an OpenAPI controller * * @param controllerKey A name to use as the first part of the cache key for this controller, must be unique amongst all controllers * @param OpenApiClass The OpenAPI controller class * @param openApiConfigOverride The configuration object for OpenAPI HTTP requests, will override the default configuration at API factory level * @returns A set of state management tools for an OpenAPI controller using Axios */ const createAxiosOpenApiController = (controllerKey, OpenApiClass, openApiConfigOverride) => { // fix scoping issues in generated client const client = (0, api_1.fixGeneratedClient)(new OpenApiClass(openApiConfigOverride !== null && openApiConfigOverride !== void 0 ? openApiConfigOverride : openApiConfig, basePath)); // Record of mock endpoints let registeredMockEndpoints = {}; /** * Merges the provided mock endpoints with the already registered mock endpoints. * @param mockEndpoints - An object containing mock endpoint functions. */ const registerMockEndpoints = (mockEndpoints) => { registeredMockEndpoints = Object.assign(Object.assign({}, registeredMockEndpoints), mockEndpoints); }; /** * Retrieves a mock endpoint function for a given endpoint key. * @param endpointKey - The key of the endpoint to retrieve the mock function for. * @returns The mock function for the given endpoint key. * @throws Will throw an error if a mock function for the given endpoint key has not been registered. */ const getMockEndpointFunction = (endpointKey) => { const mockFunc = registeredMockEndpoints[endpointKey]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- this is nonsense, it's always defined if (!mockFunc) { throw new Error(`Mock endpoint not defined for: ${controllerKey}.${endpointKey}`); } return mockFunc; }; // iterate the OpenAPI class const endpoints = Object.keys(client).reduce((memo, endpointKey) => { /** * Fetch function for server/client side use, calls the OpenAPI fetcher and returns the axios response * @param args Whatever args have been passed to the fetch, this function doesn't need to know what they are * @returns The axios response */ const fetchFactory = (enableMockingViaConfig) => (...args) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { if (enableMocking || enableMockingViaConfig) { const mockFunc = getMockEndpointFunction(endpointKey); return (0, api_1.processAxiosPromise)(() => mockFunc(...args)); } const func = client[endpointKey]; return (0, api_1.processAxiosPromise)(() => func(...args)); }); /** * Retrieves the cache key for this specific endpoint * @param additionalCacheKey Any further cache key parts to add on to the default `controllerKey.endpointKey` * @returns The final cache key string for the endpoint */ const cacheKeyGetter = (additionalCacheKey) => { let finalKeys = Array.isArray(additionalCacheKey) ? additionalCacheKey : [additionalCacheKey]; finalKeys = finalKeys.filter((key) => key !== undefined && key !== null); return (0, caching_1.cacheKeyConcat)(controllerKey, endpointKey, ...finalKeys); }; /** * Returns a `swr` mutate matcher function which will invalidate on the basis of "starts with" on the root cache key * @param additionalCacheKey Any further cache key parts to add on to the default `controllerKey.endpointKey` * @returns A `swr` mutate matcher function */ const startsWithInvalidator = (additionalCacheKey) => { return (key) => { return typeof key === 'string' && key.startsWith(cacheKeyGetter(additionalCacheKey)); }; }; const endpointId = cacheKeyGetter(); /** * The combined state management tools for this endpoint */ const endpointTools = { controllerKey, endpointKey, endpointId: (0, caching_1.cacheKeyConcat)(controllerKey, endpointKey), fetch: fetchFactory(), cacheKey: cacheKeyGetter, startsWithInvalidator, useQuery: (config) => { const fetchOverride = React.useCallback((...args) => fetchFactory(config === null || config === void 0 ? void 0 : config.enableMocking)(...args), [fetchFactory]); const _a = (0, useQuery_1.useQuery)(endpointId, fetchOverride, config, useApiProcessing, useGlobalFetchWrapper, swrConfig), { data } = _a, rest = tslib_1.__rest(_a, ["data"]); return Object.assign(Object.assign({}, rest), { data: (0, api_1.isAxiosResponse)(data) ? data.data : data }); }, useMutation: (config) => { const fetchOverride = React.useCallback((...args) => fetchFactory(config === null || config === void 0 ? void 0 : config.enableMocking)(...args), [fetchFactory]); const _a = (0, useClientFetch_1.useClientFetch)(endpointId, 'mutation', config === null || config === void 0 ? void 0 : config.fetchConfig, fetchOverride, config === null || config === void 0 ? void 0 : config.params, useApiProcessing, useGlobalFetchWrapper, config === null || config === void 0 ? void 0 : config.fetchWrapper), { data } = _a, rest = tslib_1.__rest(_a, ["data"]); return Object.assign(Object.assign({}, rest), { data: (0, api_1.isAxiosResponse)(data) ? data.data : data }); }, useInfiniteQuery: (config) => { const fetchOverride = React.useCallback((...args) => fetchFactory(config === null || config === void 0 ? void 0 : config.enableMocking)(...args), [fetchFactory]); const _a = (0, useInfiniteQuery_1.useInfiniteQuery)(endpointId, fetchOverride, config, useApiProcessing, useGlobalFetchWrapper, swrInfiniteConfig), { data } = _a, rest = tslib_1.__rest(_a, ["data"]); return Object.assign(Object.assign({}, rest), { data: data === null || data === void 0 ? void 0 : data.map((page) => ((0, api_1.isAxiosResponse)(page) ? page.data : page)) }); }, }; return Object.assign(Object.assign({}, memo), { [endpointKey]: endpointTools }); }, {}); return Object.assign(Object.assign({}, endpoints), { registerMockEndpoints }); }; return { createAxiosOpenApiController }; }; exports.axiosOpenApiControllerFactory = axiosOpenApiControllerFactory;