UNPKG

ts-retrofit

Version:

A declarative and axios based retrofit implementation for JavaScript and TypeScript.

564 lines (563 loc) 21.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServiceBuilder = exports.ResponseInterceptor = exports.RequestInterceptor = exports.BaseService = exports.nonHTTPRequestMethod = void 0; const qs = __importStar(require("qs")); const axios_1 = __importDefault(require("axios")); const dataResolver_1 = require("./dataResolver"); const util_1 = require("./util"); axios_1.default.defaults.withCredentials = true; const NON_HTTP_REQUEST_PROPERTY_NAME = "__nonHTTPRequestMethod__"; const nonHTTPRequestMethod = (target, methodName) => { const descriptor = { value: true, writable: false, }; Object.defineProperty(target[methodName], NON_HTTP_REQUEST_PROPERTY_NAME, descriptor); }; exports.nonHTTPRequestMethod = nonHTTPRequestMethod; class BaseService { constructor(serviceBuilder) { this._endpoint = serviceBuilder.endpoint; this._httpClient = new HttpClient(serviceBuilder); this._methodMap = new Map(); this._timeout = serviceBuilder.timeout; this._logCallback = serviceBuilder.logCallback; const methodNames = this._getInstanceMethodNames(); methodNames.forEach((methodName) => { this._methodMap[methodName] = this[methodName]; }); const self = this; for (const methodName of methodNames) { const descriptor = { enumerable: true, configurable: true, get() { const method = self._methodMap[methodName]; const methodOriginalDescriptor = Object.getOwnPropertyDescriptor(method, NON_HTTP_REQUEST_PROPERTY_NAME); if (methodOriginalDescriptor && methodOriginalDescriptor.value === true) { return method; } if (methodName === "_logCallback") { return method; } return (...args) => { return self._wrap(methodName, args); }; }, set: (newValue) => { self._methodMap[methodName] = newValue; }, }; Object.defineProperty(this, methodName, descriptor); } } isClientStandalone() { return this._httpClient.isStandalone(); } useRequestInterceptor(interceptor) { return this._httpClient.useRequestInterceptor(interceptor); } useResponseInterceptor(interceptor) { return this._httpClient.useResponseInterceptor(interceptor); } ejectRequestInterceptor(id) { this._httpClient.ejectRequestInterceptor(id); } ejectResponseInterceptor(id) { this._httpClient.ejectResponseInterceptor(id); } setEndpoint(endpoint) { this._endpoint = endpoint; } setLogCallback(logCallback) { this._logCallback = logCallback; } setTimeout(timeout) { this._timeout = timeout; } _getInstanceMethodNames() { let properties = []; let obj = this; do { properties = properties.concat(Object.getOwnPropertyNames(obj)); obj = Object.getPrototypeOf(obj); } while (obj); return properties.sort().filter((e, i, arr) => { return e !== arr[i + 1] && this[e] && typeof this[e] === "function"; }); } _wrap(methodName, args) { return __awaiter(this, void 0, void 0, function* () { const { url, method, headers, query, data } = this._resolveParameters(methodName, args); const config = this._makeConfig(methodName, url, method, headers, query, data); let error; let response; try { response = yield this._httpClient.sendRequest(config); } catch (err) { error = err; // @ts-ignore response = err.response; } if (this._logCallback) { this._logCallback(config, response); } if (error) { throw error; } return response; }); } _resolveParameters(methodName, args) { const url = this._resolveUrl(methodName, args); const method = this._resolveHttpMethod(methodName); let headers = this._resolveHeaders(methodName, args); const query = this._resolveQuery(methodName, args); const data = this._resolveData(methodName, headers, args); if (headers["content-type"] && headers["content-type"].indexOf("multipart/form-data") !== -1 && util_1.isNode) { headers = Object.assign(Object.assign({}, headers), data.getHeaders()); } return { url, method, headers, query, data }; } _makeConfig(methodName, url, method, headers, query, data) { let config = { url, method, headers, params: query, data, }; // response type if (this.__meta__[methodName].responseType) { config.responseType = this.__meta__[methodName].responseType; } // request transformer if (this.__meta__[methodName].requestTransformer) { config.transformRequest = this.__meta__[methodName].requestTransformer; } // response transformer if (this.__meta__[methodName].responseTransformer) { config.transformResponse = this.__meta__[methodName].responseTransformer; } // timeout config.timeout = this.__meta__[methodName].timeout || this._timeout; // deprecated if (this.__meta__[methodName].deprecated) { let hint = `[warning] Deprecated method: "${methodName}". `; if (this.__meta__[methodName].deprecatedHint) { hint += this.__meta__[methodName].deprecatedHint; } // tslint:disable-next-line:no-console console.warn(hint); } // query array format if (this.__meta__[methodName].queryArrayFormat) { config.paramsSerializer = (params) => { return qs.stringify(params, { arrayFormat: this.__meta__[methodName].queryArrayFormat }); }; } // mix in config set by @Config config = Object.assign(Object.assign({}, config), this.__meta__[methodName].config); return config; } _resolveUrl(methodName, args) { const meta = this.__meta__; const endpoint = this._endpoint; const basePath = meta.basePath; const path = meta[methodName].path; const pathParams = meta[methodName].pathParams; const options = meta[methodName].options || {}; let url = this.makeURL(endpoint, basePath, path, options); for (const pos in pathParams) { if (pathParams[pos]) { url = url.replace(new RegExp(`\{${pathParams[pos]}}`), args[pos]); } } return url; } makeURL(endpoint, basePath, path, options) { const isAbsoluteURL = /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(path); if (isAbsoluteURL) { return path; } if (options.ignoreBasePath) { return [endpoint, path].join(""); } return [endpoint, basePath, path].join(""); } _resolveHttpMethod(methodName) { const meta = this.__meta__; return meta[methodName].method; } _resolveHeaders(methodName, args) { const meta = this.__meta__; const headers = meta[methodName].headers || {}; const headerParams = meta[methodName].headerParams; for (const pos in headerParams) { if (headerParams[pos]) { headers[headerParams[pos]] = args[pos]; } } const headerMapIndex = meta[methodName].headerMapIndex; if (headerMapIndex >= 0) { for (const key in args[headerMapIndex]) { if (args[headerMapIndex][key]) { headers[key] = args[headerMapIndex][key]; } } } return headers; } _resolveQuery(methodName, args) { const meta = this.__meta__; const query = meta[methodName].query || {}; const queryParams = meta[methodName].queryParams; for (const pos in queryParams) { if (queryParams[pos]) { query[queryParams[pos]] = args[pos]; } } const queryMapIndex = meta[methodName].queryMapIndex; if (queryMapIndex >= 0) { for (const key in args[queryMapIndex]) { if (args[queryMapIndex][key]) { query[key] = args[queryMapIndex][key]; } } } return query; } _resolveData(methodName, headers, args) { const meta = this.__meta__; const bodyIndex = meta[methodName].bodyIndex; const fields = meta[methodName].fields || {}; const parts = meta[methodName].parts || {}; const fieldMapIndex = meta[methodName].fieldMapIndex; const gqlQuery = meta[methodName].gqlQuery; const gqlOperationName = meta[methodName].gqlOperationName; const gqlVariablesIndex = meta[methodName].gqlVariablesIndex; let data = {}; // @Body if (bodyIndex >= 0) { if (Array.isArray(args[bodyIndex])) { data = args[bodyIndex]; } else { data = Object.assign(Object.assign({}, data), args[bodyIndex]); } } // @Field if (Object.keys(fields).length > 0) { const reqData = {}; for (const pos in fields) { if (fields[pos]) { reqData[fields[pos]] = args[pos]; } } data = Object.assign(Object.assign({}, data), reqData); } // @FieldMap if (fieldMapIndex >= 0) { const reqData = {}; for (const key in args[fieldMapIndex]) { if (args[fieldMapIndex][key]) { reqData[key] = args[fieldMapIndex][key]; } } data = Object.assign(Object.assign({}, data), reqData); } // @MultiPart if (Object.keys(parts).length > 0) { const reqData = {}; for (const pos in parts) { if (parts[pos]) { reqData[parts[pos]] = args[pos]; } } data = Object.assign(Object.assign({}, data), reqData); } // @GraphQL if (gqlQuery) { data.query = gqlQuery; if (gqlOperationName) { data.operationName = gqlOperationName; } // @GraphQLVariables if (gqlVariablesIndex >= 0) { data.variables = args[gqlVariablesIndex]; } } const contentType = headers["content-type"] || "application/json"; const dataResolverFactory = new dataResolver_1.DataResolverFactory(); const dataResolver = dataResolverFactory.createDataResolver(contentType); return dataResolver.resolve(headers, data); } } __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Boolean) ], BaseService.prototype, "isClientStandalone", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Function]), __metadata("design:returntype", Number) ], BaseService.prototype, "useRequestInterceptor", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Function]), __metadata("design:returntype", Number) ], BaseService.prototype, "useResponseInterceptor", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Number]), __metadata("design:returntype", void 0) ], BaseService.prototype, "ejectRequestInterceptor", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Number]), __metadata("design:returntype", void 0) ], BaseService.prototype, "ejectResponseInterceptor", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", void 0) ], BaseService.prototype, "setEndpoint", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], BaseService.prototype, "setLogCallback", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Number]), __metadata("design:returntype", void 0) ], BaseService.prototype, "setTimeout", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Array) ], BaseService.prototype, "_getInstanceMethodNames", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Array]), __metadata("design:returntype", Promise) ], BaseService.prototype, "_wrap", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Array]), __metadata("design:returntype", Object) ], BaseService.prototype, "_resolveParameters", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, String, String, Object, Object, Object]), __metadata("design:returntype", Object) ], BaseService.prototype, "_makeConfig", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Array]), __metadata("design:returntype", String) ], BaseService.prototype, "_resolveUrl", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, String, String, Object]), __metadata("design:returntype", String) ], BaseService.prototype, "makeURL", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", String) ], BaseService.prototype, "_resolveHttpMethod", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Array]), __metadata("design:returntype", Object) ], BaseService.prototype, "_resolveHeaders", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Array]), __metadata("design:returntype", Object) ], BaseService.prototype, "_resolveQuery", null); __decorate([ exports.nonHTTPRequestMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object, Array]), __metadata("design:returntype", Object) ], BaseService.prototype, "_resolveData", null); exports.BaseService = BaseService; class BaseInterceptor { onRejected(error) { return; } } class RequestInterceptor extends BaseInterceptor { } exports.RequestInterceptor = RequestInterceptor; class ResponseInterceptor extends BaseInterceptor { } exports.ResponseInterceptor = ResponseInterceptor; class ServiceBuilder { constructor() { this._endpoint = ""; this._standalone = false; this._requestInterceptors = []; this._responseInterceptors = []; this._timeout = 60000; this._logCallback = null; } build(service) { return new service(this); } setEndpoint(endpoint) { this._endpoint = endpoint; return this; } setStandalone(standalone) { this._standalone = standalone; return this; } setRequestInterceptors(...interceptors) { this._requestInterceptors.push(...interceptors); return this; } setResponseInterceptors(...interceptors) { this._responseInterceptors.push(...interceptors); return this; } setTimeout(timeout) { this._timeout = timeout; return this; } setLogCallback(logCallback) { this._logCallback = logCallback; return this; } get endpoint() { return this._endpoint; } get standalone() { return this._standalone; } get requestInterceptors() { return this._requestInterceptors; } get responseInterceptors() { return this._responseInterceptors; } get timeout() { return this._timeout; } get logCallback() { return this._logCallback; } } exports.ServiceBuilder = ServiceBuilder; class HttpClient { constructor(builder) { this.axios = axios_1.default; this.standalone = false; if (builder.standalone === true) { this.axios = axios_1.default.create(); this.standalone = true; } else if (typeof builder.standalone === "function") { this.axios = builder.standalone; } builder.requestInterceptors.forEach((interceptor) => { if (interceptor instanceof RequestInterceptor) { this.axios.interceptors.request.use(interceptor.onFulfilled.bind(interceptor), interceptor.onRejected.bind(interceptor)); } else { this.axios.interceptors.request.use(interceptor); } }); builder.responseInterceptors.forEach((interceptor) => { if (interceptor instanceof ResponseInterceptor) { this.axios.interceptors.response.use(interceptor.onFulfilled.bind(interceptor), interceptor.onRejected.bind(interceptor)); } else { this.axios.interceptors.response.use(interceptor); } }); } isStandalone() { return this.standalone; } sendRequest(config) { return __awaiter(this, void 0, void 0, function* () { return this.axios(config); }); } useRequestInterceptor(interceptor) { return this.axios.interceptors.request.use(interceptor); } useResponseInterceptor(interceptor) { return this.axios.interceptors.response.use(interceptor); } ejectRequestInterceptor(id) { this.axios.interceptors.request.eject(id); } ejectResponseInterceptor(id) { this.axios.interceptors.response.eject(id); } }