@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
JavaScript
;
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;