ts-retrofit3
Version:
A declarative and axios based retrofit implementation for JavaScript and TypeScript.
627 lines (626 loc) • 24.5 kB
JavaScript
"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, signal, extraMap, onUploadProgress } = this._resolveParameters(methodName, args);
const config = this._makeConfig(methodName, url, method, headers, query, data, signal, extraMap, onUploadProgress);
let error;
let response;
try {
response = yield this._httpClient.sendRequest(config);
// @ts-ignore
if ((response === null || response === void 0 ? void 0 : response.name) === "AxiosError") {
throw response;
}
}
catch (err) {
error = err;
// @ts-ignore
response = err.response;
}
if (this._logCallback) {
this._logCallback(config, response);
}
if (error) {
throw error;
}
return Object.assign(Object.assign({}, response), { extraMap });
});
}
_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);
const onUploadProgress = this._resolveOnUploadProgress(methodName, args);
const signal = this._resolveSignal(methodName, args);
const extraMap = this._resolveExtraMap(methodName, 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, signal, extraMap, onUploadProgress };
}
_makeConfig(methodName, url, method, headers, query, data, signal, extraMap, onUploadProgress) {
let config = {
url,
method,
headers,
params: query,
data,
signal,
extraMap,
onUploadProgress,
};
// 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 });
};
}
if (this.__meta__[methodName].onUploadProgress) {
config.onUploadProgress = this.__meta__[methodName].onUploadProgress;
}
// 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] !== undefined && queryParams[pos] !== null) {
query[queryParams[pos]] = args[pos];
}
}
const queryMapIndex = meta[methodName].queryMapIndex;
if (queryMapIndex >= 0) {
for (const key in args[queryMapIndex]) {
if (args[queryMapIndex][key] !== undefined && args[queryMapIndex][key] !== null) {
query[key] = args[queryMapIndex][key];
}
}
}
return query;
}
_resolveSignal(methodName, args) {
var _a;
const meta = this.__meta__;
const signalIndex = ((_a = meta[methodName]) === null || _a === void 0 ? void 0 : _a.signalIndex) || {};
if (signalIndex >= 0) {
return args[signalIndex];
}
return null;
}
_resolveExtraMap(methodName, args) {
var _a;
const meta = this.__meta__;
const extraMapIndex = ((_a = meta[methodName]) === null || _a === void 0 ? void 0 : _a.extraMapIndex) || {};
if (extraMapIndex >= 0) {
return args[extraMapIndex];
}
return null;
}
_resolveOnUploadProgress(methodName, args) {
var _a, _b;
const meta = this.__meta__;
const onUploadProgressIndex = (_b = (_a = meta[methodName]) === null || _a === void 0 ? void 0 : _a.onUploadProgressIndex) !== null && _b !== void 0 ? _b : {};
if (onUploadProgressIndex > 0) {
return args[onUploadProgressIndex];
}
return null;
}
_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, 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, Array]),
__metadata("design:returntype", Object)
], BaseService.prototype, "_resolveSignal", null);
__decorate([
exports.nonHTTPRequestMethod,
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Array]),
__metadata("design:returntype", Object)
], BaseService.prototype, "_resolveExtraMap", null);
__decorate([
exports.nonHTTPRequestMethod,
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Array]),
__metadata("design:returntype", Object)
], BaseService.prototype, "_resolveOnUploadProgress", 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);
}
}