UNPKG

@inventorjs/api-service

Version:
78 lines (77 loc) 3.53 kB
import axios from 'axios'; import { defer, lastValueFrom, retry, timer } from 'rxjs'; import { mergeConfig, processConfig } from './util.js'; import { CONFIG_META, INSTANCE_META } from './constants.js'; import { defaults } from './defaults.js'; export class ApiService { static rcChannelMap = new Map(); static inited = false; static async init({ services, config: rootConfig = {}, }) { if (!Object.keys(services).length) { console.error('There is no service to initialize. please check services config'); return; } this.inited = true; Object.values(services).forEach((Srv) => { const serviceConfig = Reflect.getMetadata(CONFIG_META, Srv); if (!serviceConfig) { throw new Error(`Service class must decorated by @Service, please check [${Srv}]`); } const definedConfig = mergeConfig(defaults, rootConfig, serviceConfig); const instance = axios.create(definedConfig); const extConfig = definedConfig.$ext; if (extConfig?.requestInterceptors) { extConfig?.requestInterceptors.forEach((RequestInterceptor) => { const requestInterceptor = new RequestInterceptor(); instance.interceptors.request.use(requestInterceptor.onFulfilled, requestInterceptor.onRejected, requestInterceptor.options); }); } if (extConfig?.responseInterceptors) { extConfig?.responseInterceptors.forEach((ResponseInterceptor) => { const responseInterceptor = new ResponseInterceptor(); instance.interceptors.response.use(responseInterceptor.onFulfilled, responseInterceptor.onRejected, responseInterceptor.options); }); } Reflect.defineMetadata(INSTANCE_META, instance, Srv); }); } static async apiCall(data, requestConfig = {}) { const instance = Reflect.getMetadata(INSTANCE_META, this); if (!instance) { throw new Error(`Service instance not exist for ${this.name}, service must decorated by @Service`); } const instanceConfig = instance.defaults; const mergedConfig = mergeConfig(instanceConfig, requestConfig); const config = processConfig(mergedConfig, data); const extConfig = config.$ext; const genReqId = extConfig?.genReqId; const reqId = genReqId?.(config); config.$runtime ??= {}; config.$runtime.reqId = reqId; const rcChannel = extConfig?.rcChannel; if (rcChannel) { ApiService.rcChannelMap.set(rcChannel, reqId); } const stream$ = defer(() => instance.request(config)).pipe(retry({ count: extConfig?.retry ?? 0, delay: (_, retryCount) => { config.$runtime ??= {}; config.$runtime.retryCount = retryCount; return timer(Math.min(2 ** retryCount, 30) * 1000); }, })); const response = await lastValueFrom(stream$); if (rcChannel && ApiService.rcChannelMap.get(rcChannel) !== reqId) { return new Promise(() => { // disccard race condition conflict result }); } if (rcChannel) { ApiService.rcChannelMap.delete(rcChannel); } if (extConfig?.observe === 'response') { return response; } return response?.data; } }