UNPKG

@sword-health/ui-http-mapper

Version:

UI clients tool for consumption of easy to read/config endpoint maps

136 lines (108 loc) 4.77 kB
const defaultConfigs = { logPrefix: '[sh-http]', supportedMethods: ['get', 'post', 'put', 'delete', 'head', 'patch', 'options'], }; // Workaround for array based dynamic type creation const auxDynamicMethodTypesCreation = Object.assign({}, ...defaultConfigs.supportedMethods.map(t => ({[t]: ''})) ); type requestMethod = keyof typeof auxDynamicMethodTypesCreation; interface RequestData { method: requestMethod; url: string; requestArgs: Array<object>; meta: object; } interface EndpointsConfigs { data?: object; modules: object; } interface EndpointRequirements { $$method: requestMethod; $$makeUrl: Function; $$config: object; [key: string]: any, } type FoundEnpointConfig = EndpointRequirements | void; export default class EndpointDecoderTS { endpointsSpec: EndpointsConfigs; constructor(endpointsMap: EndpointsConfigs = { modules: {} }, configs: object = {}) { this.endpointsSpec = endpointsMap; } private _makeNamespacePath(namespace: string): Array<string> { const safeNameSpace = namespace.replace(/(\/{2})|(\/$)/, ''); return safeNameSpace.split('/'); } private _findEndpointConfigs(namespace: string, endpointsConfigsMap: object = {}): FoundEnpointConfig { const pathSteps = this._makeNamespacePath(namespace); if (!pathSteps.length) { return; } const pathToConsider = pathSteps.join('/'); const goNextStep = (steps: Array<string>, currentConfigs: object, currIndex: number = 0): FoundEnpointConfig => { const stepConfigs = currentConfigs[steps[currIndex]]; if (!stepConfigs) { console.error(`${defaultConfigs.logPrefix} Unable to find configs for '${pathToConsider}'`); return; } // Check if should end querying const reachFinalStep = steps.length - 1 === currIndex; // Keep finding until reaching end of given path if (!reachFinalStep) { // eslint-disable-next-line consistent-return return goNextStep(steps, stepConfigs, currIndex + 1); } // Valid if found path is a complete one const validFinalPath = ('$$makeUrl' in stepConfigs) && ('$$method' in stepConfigs); if (!validFinalPath) { console.error(`${defaultConfigs.logPrefix} Given path '${pathToConsider}' incomplete.`); return; } // eslint-disable-next-line consistent-return return stepConfigs; }; // eslint-disable-next-line consistent-return return goNextStep(pathSteps, endpointsConfigsMap); } set dataUpdate(freshDataObject: object) { this.endpointsSpec.data = freshDataObject; } $http(namespace: string, urlData: any, { body: requestPayload, config: requestConfigs }: { body?: object, config?: object } = {config: {}}): RequestData { // Validate namespace format const validNameSpace = typeof namespace === 'string'; if (!validNameSpace) { console.error(`${defaultConfigs.logPrefix} Invalid namespace provided. Aborting`); return; } // Find requested endpoint configuration const endpointConfigs = this._findEndpointConfigs(namespace, this.endpointsSpec.modules); if (!endpointConfigs) { return; } const { $$method: method, $$makeUrl: makeUrl, $$config: defaultConfig = {}, $$meta: meta = {} } = endpointConfigs; // Check if what config from endpoint spec are executable and run them now const defaultConfigExec = {}; Object.keys(defaultConfig).forEach(configKey => { const configVal = defaultConfig[configKey]; const executableConfig = (typeof configVal === 'function'); if (executableConfig) { defaultConfigExec[configKey] = configVal(this.endpointsSpec.data); return; } defaultConfigExec[configKey] = configVal; }); // Merge default configs with adhoc one, giving priority to adhoc ones const mergedConfig = Object.assign({}, defaultConfigExec, requestConfigs); // Make endpoint url (will use default base url) const url = makeUrl(endpointConfigs, urlData); // Appending request payload to requests that support it const requestArgs = [mergedConfig]; if (method === 'put' || method === 'post' || method === 'patch' || method === 'delete') { requestArgs.unshift(requestPayload); } return { method, url, requestArgs, meta, } } }