@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.
122 lines (121 loc) • 8.16 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.genericApiControllerFactory = 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 caching_1 = require("../utils/caching");
const useInfiniteQuery_1 = require("../hooks/useInfiniteQuery");
const config_1 = require("../utils/config");
/**
* Creates a factory of state management tools from a generic API controller object.
*
* @param {TConfig} globalFetchConfig - Optional custom fetch config to pass to all API calls. Can be overridden at endpoint and fetch level.
* @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 {IApiControllerFactory} A library of controller factory methods that create state management tools for a generic controller.
*/
const genericApiControllerFactory = ({ globalFetchConfig, 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 controller The controller object
* @param controllerConfig Optional custom fetch config to pass to all API calls. Inherits global config and can be overridden at fetch level.
* @returns A set of state management tools for an OpenAPI controller using Axios
*/
const createGenericApiController = (controllerKey, controller, controllerConfig) => {
// 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 controller object
const endpoints = Object.keys(controller).reduce((memo, endpointKey) => {
/**
* Fetch function for server/client side use, calls the fetcher
* @param args Whatever args have been passed to the fetch, this function doesn't need to know what they are
* @returns The response data
*/
const fetchFactory = (enableMockingViaConfig) => (...args) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
if (enableMocking || enableMockingViaConfig) {
const mockFunc = getMockEndpointFunction(endpointKey);
return mockFunc(...args);
}
const func = controller[endpointKey];
return 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: (params, config) => { var _a; return fetchFactory()(params, (_a = (0, config_1.combineConfigs)({ fetchConfig: config }, globalFetchConfig, controllerConfig)) === null || _a === void 0 ? void 0 : _a.fetchConfig); },
cacheKey: cacheKeyGetter,
startsWithInvalidator,
useQuery: (config) => {
const fetchOverride = React.useCallback((...args) => fetchFactory(config === null || config === void 0 ? void 0 : config.enableMocking)(...args), [fetchFactory]);
return (0, useQuery_1.useQuery)(endpointId, fetchOverride, (0, config_1.combineConfigs)(config, globalFetchConfig, controllerConfig), useApiProcessing, useGlobalFetchWrapper, swrConfig);
},
useMutation: (config) => {
var _a;
const fetchOverride = React.useCallback((...args) => fetchFactory(config === null || config === void 0 ? void 0 : config.enableMocking)(...args), [fetchFactory]);
return (0, useClientFetch_1.useClientFetch)(endpointId, 'mutation', (_a = (0, config_1.combineConfigs)(config, globalFetchConfig, controllerConfig)) === null || _a === void 0 ? void 0 : _a.fetchConfig, fetchOverride, config === null || config === void 0 ? void 0 : config.params, useApiProcessing, useGlobalFetchWrapper, config === null || config === void 0 ? void 0 : config.fetchWrapper);
},
useInfiniteQuery: (config) => {
const fetchOverride = React.useCallback((...args) => fetchFactory(config === null || config === void 0 ? void 0 : config.enableMocking)(...args), [fetchFactory]);
return (0, useInfiniteQuery_1.useInfiniteQuery)(endpointId, fetchOverride, (0, config_1.combineConfigs)(config, globalFetchConfig, controllerConfig), useApiProcessing, useGlobalFetchWrapper, swrInfiniteConfig);
},
};
return Object.assign(Object.assign({}, memo), { [endpointKey]: endpointTools });
}, {});
return Object.assign(Object.assign({}, endpoints), { registerMockEndpoints });
};
return { createGenericApiController };
};
exports.genericApiControllerFactory = genericApiControllerFactory;