@proteinjs/service 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/src/Service.d.ts +8 -2
- package/dist/src/Service.d.ts.map +1 -1
- package/dist/src/Service.js +31 -87
- package/dist/src/Service.js.map +1 -1
- package/dist/src/ServiceClient.d.ts +2 -1
- package/dist/src/ServiceClient.d.ts.map +1 -1
- package/dist/src/ServiceClient.js +38 -4
- package/dist/src/ServiceClient.js.map +1 -1
- package/dist/src/ServiceRouter.js +2 -2
- package/dist/src/ServiceRouter.js.map +1 -1
- package/package.json +2 -2
- package/src/Service.ts +41 -24
- package/src/ServiceClient.ts +20 -3
- package/src/ServiceRouter.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.2.0](https://github.com/proteinjs/service/compare/@proteinjs/service@1.1.1...@proteinjs/service@1.2.0) (2025-02-06)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* fix typing for the debounce config ([62bb2d6](https://github.com/proteinjs/service/commit/62bb2d646e2c71f2bf5300364cd92bbd95911f21))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* method specific debouncers. allows one service to use multiple debouncers for each method in the service. ([00fa7b2](https://github.com/proteinjs/service/commit/00fa7b23c4960ad9a006d4791e9f5a12f9ab6c89))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [1.1.1](https://github.com/proteinjs/service/compare/@proteinjs/service@1.1.0...@proteinjs/service@1.1.1) (2024-09-10)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* move retry logic into ServiceClient. send 400s in ServiceRouter if there is an error in the routing. ([742aa15](https://github.com/proteinjs/service/commit/742aa15ce505f115e94093fc96e6cac811aaf83e))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
6
33
|
# [1.1.0](https://github.com/proteinjs/service/compare/@proteinjs/service@1.0.32...@proteinjs/service@1.1.0) (2024-09-09)
|
|
7
34
|
|
|
8
35
|
|
package/dist/src/Service.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ type KeysWithoutService<T extends Service> = Diff<KeysWithoutIndexSignature<T>,
|
|
|
10
10
|
type RetryConfig<T extends Service> = {
|
|
11
11
|
[K in KeysWithoutService<T>]?: number;
|
|
12
12
|
};
|
|
13
|
+
interface DebounceConfig {
|
|
14
|
+
waitTime: number;
|
|
15
|
+
}
|
|
16
|
+
type MethodDebounceConfig<T extends Service> = {
|
|
17
|
+
[K in KeysWithoutService<T>]?: DebounceConfig;
|
|
18
|
+
};
|
|
13
19
|
export interface Service extends Loadable {
|
|
14
20
|
serviceMetadata?: {
|
|
15
21
|
auth?: {
|
|
@@ -36,9 +42,9 @@ export interface Service extends Loadable {
|
|
|
36
42
|
* Create a factory that creates an instance of the Service. The Service instance is a
|
|
37
43
|
* ServiceClient wrapped in the interface's api.
|
|
38
44
|
* @param serviceInterfaceQualifiedName the package-qualified name of the service interface (ie. service-package-name/MyService)
|
|
39
|
-
* @param debouncer
|
|
45
|
+
* @param debouncer pass in either a single debouncer instance or method-specific debounce configurations
|
|
40
46
|
* @returns a function that creates a Service
|
|
41
47
|
*/
|
|
42
|
-
export declare const serviceFactory: <T extends Service>(serviceInterfaceQualifiedName: string, debouncer?: Debouncer, retryConfig?: RetryConfig<T> | undefined) => () => T;
|
|
48
|
+
export declare const serviceFactory: <T extends Service>(serviceInterfaceQualifiedName: string, debouncer?: Debouncer | MethodDebounceConfig<T> | undefined, retryConfig?: RetryConfig<T> | undefined) => () => T;
|
|
43
49
|
export {};
|
|
44
50
|
//# sourceMappingURL=Service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAgB,MAAM,iBAAiB,CAAC;AAE1D,KAAK,WAAW,CAAC,CAAC,IAAI;KACnB,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC3G,CAAC;AACF,KAAK,yBAAyB,CAAC,CAAC,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;AAGzD,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAG1C,KAAK,kBAAkB,CAAC,CAAC,SAAS,OAAO,IAAI,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpH,KAAK,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI;KACnC,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM;CACtC,CAAC;AAEF,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,oBAAoB,CAAC,CAAC,SAAS,OAAO,IAAI;KAC5C,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,cAAc;CAC9C,CAAC;AAEF,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,eAAe,CAAC,EAAE;QAChB,IAAI,CAAC,EAAE;YACL,2HAA2H;YAC3H,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,kIAAkI;YAClI,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,8HAA8H;YAC9H,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YACjB;;;;;eAKG;YACH,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;SAC1D,CAAC;QACF,uFAAuF;QACvF,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;CACzD;AAaD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,qDACM,MAAM,mHAkCtC,CAAC"}
|
package/dist/src/Service.js
CHANGED
|
@@ -1,108 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
3
|
exports.serviceFactory = void 0;
|
|
40
4
|
var reflection_1 = require("@proteinjs/reflection");
|
|
41
5
|
var ServiceClient_1 = require("./ServiceClient");
|
|
6
|
+
var util_1 = require("@proteinjs/util");
|
|
7
|
+
var debouncerMap = {};
|
|
8
|
+
/** Retrieve an existing debouncer from the map or make a new one.
|
|
9
|
+
* This allows the debouncer instance per method to remain the same across multiple service calls.
|
|
10
|
+
*/
|
|
11
|
+
function getOrCreateDebouncer(methodName, waitTime) {
|
|
12
|
+
if (!debouncerMap[methodName]) {
|
|
13
|
+
debouncerMap[methodName] = new util_1.Debouncer(waitTime);
|
|
14
|
+
}
|
|
15
|
+
return debouncerMap[methodName];
|
|
16
|
+
}
|
|
42
17
|
/**
|
|
43
18
|
* Create a factory that creates an instance of the Service. The Service instance is a
|
|
44
19
|
* ServiceClient wrapped in the interface's api.
|
|
45
20
|
* @param serviceInterfaceQualifiedName the package-qualified name of the service interface (ie. service-package-name/MyService)
|
|
46
|
-
* @param debouncer
|
|
21
|
+
* @param debouncer pass in either a single debouncer instance or method-specific debounce configurations
|
|
47
22
|
* @returns a function that creates a Service
|
|
48
23
|
*/
|
|
49
24
|
var serviceFactory = function (serviceInterfaceQualifiedName, debouncer, retryConfig) {
|
|
50
25
|
return function () {
|
|
51
26
|
var service = {};
|
|
52
27
|
var serviceInterface = reflection_1.SourceRepository.get().interface(serviceInterfaceQualifiedName);
|
|
53
|
-
var
|
|
28
|
+
for (var _i = 0, _a = serviceInterface.methods; _i < _a.length; _i++) {
|
|
29
|
+
var method = _a[_i];
|
|
54
30
|
var servicePath = "/service/".concat(serviceInterface.qualifiedName, "/").concat(method.name);
|
|
55
|
-
var
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
service[method.name] = function () {
|
|
60
|
-
var args = [];
|
|
61
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
62
|
-
args[_i] = arguments[_i];
|
|
63
|
-
}
|
|
64
|
-
return __awaiter(void 0, void 0, void 0, function () {
|
|
65
|
-
var lastError, attempt, error_1;
|
|
66
|
-
return __generator(this, function (_a) {
|
|
67
|
-
switch (_a.label) {
|
|
68
|
-
case 0:
|
|
69
|
-
attempt = 0;
|
|
70
|
-
_a.label = 1;
|
|
71
|
-
case 1:
|
|
72
|
-
if (!(attempt < maxRetries_1)) return [3 /*break*/, 7];
|
|
73
|
-
_a.label = 2;
|
|
74
|
-
case 2:
|
|
75
|
-
_a.trys.push([2, 3, , 6]);
|
|
76
|
-
return [2 /*return*/, serviceClient.send.bind(serviceClient).apply(void 0, args)];
|
|
77
|
-
case 3:
|
|
78
|
-
error_1 = _a.sent();
|
|
79
|
-
lastError = error_1;
|
|
80
|
-
if (!(attempt < maxRetries_1 - 1)) return [3 /*break*/, 5];
|
|
81
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
82
|
-
case 4:
|
|
83
|
-
_a.sent(); // short wait before retrying
|
|
84
|
-
_a.label = 5;
|
|
85
|
-
case 5: return [3 /*break*/, 6];
|
|
86
|
-
case 6:
|
|
87
|
-
attempt++;
|
|
88
|
-
return [3 /*break*/, 1];
|
|
89
|
-
case 7:
|
|
90
|
-
if (lastError) {
|
|
91
|
-
throw lastError;
|
|
92
|
-
}
|
|
93
|
-
return [2 /*return*/];
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
};
|
|
31
|
+
var methodName = method.name;
|
|
32
|
+
var retryCount = 0;
|
|
33
|
+
if (retryConfig && methodName in retryConfig) {
|
|
34
|
+
retryCount = retryConfig[methodName];
|
|
98
35
|
}
|
|
99
|
-
|
|
100
|
-
|
|
36
|
+
var methodDebouncer = void 0;
|
|
37
|
+
if (debouncer) {
|
|
38
|
+
if ((0, util_1.isInstanceOf)(debouncer, util_1.Debouncer)) {
|
|
39
|
+
methodDebouncer = debouncer;
|
|
40
|
+
}
|
|
41
|
+
else if (methodName in debouncer) {
|
|
42
|
+
var methodConfig = debouncer[methodName];
|
|
43
|
+
if (methodConfig) {
|
|
44
|
+
methodDebouncer = getOrCreateDebouncer(methodName, methodConfig.waitTime);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
101
47
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
var method = _a[_i];
|
|
105
|
-
_loop_1(method);
|
|
48
|
+
var serviceClient = new ServiceClient_1.ServiceClient(servicePath, method, methodDebouncer, retryCount);
|
|
49
|
+
service[methodName] = serviceClient.send.bind(serviceClient);
|
|
106
50
|
}
|
|
107
51
|
return service;
|
|
108
52
|
};
|
package/dist/src/Service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":";;;AACA,oDAAmE;AACnE,iDAAgD;AAChD,wCAA0D;AAgD1D,IAAM,YAAY,GAAiC,EAAE,CAAC;AACtD;;GAEG;AACH,SAAS,oBAAoB,CAAC,UAAkB,EAAE,QAAgB;IAChE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE;QAC7B,YAAY,CAAC,UAAU,CAAC,GAAG,IAAI,gBAAS,CAAC,QAAQ,CAAC,CAAC;KACpD;IACD,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACI,IAAM,cAAc,GAAG,UAC5B,6BAAqC,EACrC,SAA+C,EAC/C,WAA4B;IAE5B,OAAO;QACL,IAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,IAAM,gBAAgB,GAAG,6BAAgB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACzF,KAAqB,UAAwB,EAAxB,KAAA,gBAAgB,CAAC,OAAO,EAAxB,cAAwB,EAAxB,IAAwB,EAAE;YAA1C,IAAM,MAAM,SAAA;YACf,IAAM,WAAW,GAAG,mBAAY,gBAAgB,CAAC,aAAa,cAAI,MAAM,CAAC,IAAI,CAAE,CAAC;YAChF,IAAM,UAAU,GAAG,MAAM,CAAC,IAA6B,CAAC;YAExD,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,WAAW,IAAI,UAAU,IAAI,WAAW,EAAE;gBAC5C,UAAU,GAAG,WAAW,CAAC,UAAU,CAAE,CAAC;aACvC;YAED,IAAI,eAAe,SAAuB,CAAC;YAC3C,IAAI,SAAS,EAAE;gBACb,IAAI,IAAA,mBAAY,EAAC,SAAS,EAAE,gBAAS,CAAC,EAAE;oBACtC,eAAe,GAAG,SAAsB,CAAC;iBAC1C;qBAAM,IAAI,UAAU,IAAI,SAAS,EAAE;oBAClC,IAAM,YAAY,GAAI,SAAqC,CAAC,UAAU,CAAC,CAAC;oBACxE,IAAI,YAAY,EAAE;wBAChB,eAAe,GAAG,oBAAoB,CAAC,UAAoB,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;qBACrF;iBACF;aACF;YAED,IAAM,aAAa,GAAG,IAAI,6BAAa,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;YAC1F,OAAO,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;SAC9D;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC,CAAC;AAnCW,QAAA,cAAc,kBAmCzB"}
|
|
@@ -4,8 +4,9 @@ export declare class ServiceClient {
|
|
|
4
4
|
private servicePath;
|
|
5
5
|
private serviceMethod;
|
|
6
6
|
private debouncer?;
|
|
7
|
+
private retryCount;
|
|
7
8
|
private static requestCounter;
|
|
8
|
-
constructor(servicePath: string, serviceMethod: Method, debouncer?: Debouncer | undefined);
|
|
9
|
+
constructor(servicePath: string, serviceMethod: Method, debouncer?: Debouncer | undefined, retryCount?: number);
|
|
9
10
|
send(...args: any[]): Promise<any>;
|
|
10
11
|
private _send;
|
|
11
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceClient.d.ts","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,qBAAa,aAAa;IAItB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ServiceClient.d.ts","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,qBAAa,aAAa;IAItB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,SAAS,CAAC;IAClB,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAK;gBAGxB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,uBAAW,EACrB,UAAU,GAAE,MAAU;IAG1B,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;YA0C1B,KAAK;CAsBpB"}
|
|
@@ -40,10 +40,12 @@ exports.ServiceClient = void 0;
|
|
|
40
40
|
var serializer_1 = require("@proteinjs/serializer");
|
|
41
41
|
var isVoidReturnType_1 = require("./isVoidReturnType");
|
|
42
42
|
var ServiceClient = /** @class */ (function () {
|
|
43
|
-
function ServiceClient(servicePath, serviceMethod, debouncer) {
|
|
43
|
+
function ServiceClient(servicePath, serviceMethod, debouncer, retryCount) {
|
|
44
|
+
if (retryCount === void 0) { retryCount = 0; }
|
|
44
45
|
this.servicePath = servicePath;
|
|
45
46
|
this.serviceMethod = serviceMethod;
|
|
46
47
|
this.debouncer = debouncer;
|
|
48
|
+
this.retryCount = retryCount;
|
|
47
49
|
}
|
|
48
50
|
ServiceClient.prototype.send = function () {
|
|
49
51
|
var args = [];
|
|
@@ -51,7 +53,7 @@ var ServiceClient = /** @class */ (function () {
|
|
|
51
53
|
args[_i] = arguments[_i];
|
|
52
54
|
}
|
|
53
55
|
return __awaiter(this, void 0, void 0, function () {
|
|
54
|
-
var sendRequest;
|
|
56
|
+
var sendRequest, executeWithRetry, executeRequest;
|
|
55
57
|
var _this = this;
|
|
56
58
|
return __generator(this, function (_a) {
|
|
57
59
|
sendRequest = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
@@ -76,11 +78,43 @@ var ServiceClient = /** @class */ (function () {
|
|
|
76
78
|
}
|
|
77
79
|
});
|
|
78
80
|
}); };
|
|
81
|
+
executeWithRetry = function (fn) { return __awaiter(_this, void 0, void 0, function () {
|
|
82
|
+
var maxAttempts, attempt, error_1;
|
|
83
|
+
return __generator(this, function (_a) {
|
|
84
|
+
switch (_a.label) {
|
|
85
|
+
case 0:
|
|
86
|
+
maxAttempts = 1 + this.retryCount;
|
|
87
|
+
attempt = 0;
|
|
88
|
+
_a.label = 1;
|
|
89
|
+
case 1:
|
|
90
|
+
if (!(attempt < maxAttempts)) return [3 /*break*/, 7];
|
|
91
|
+
_a.label = 2;
|
|
92
|
+
case 2:
|
|
93
|
+
_a.trys.push([2, 4, , 6]);
|
|
94
|
+
return [4 /*yield*/, fn()];
|
|
95
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
96
|
+
case 4:
|
|
97
|
+
error_1 = _a.sent();
|
|
98
|
+
if (attempt === maxAttempts - 1) {
|
|
99
|
+
throw error_1;
|
|
100
|
+
}
|
|
101
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
102
|
+
case 5:
|
|
103
|
+
_a.sent(); // 1 second delay before retry
|
|
104
|
+
return [3 /*break*/, 6];
|
|
105
|
+
case 6:
|
|
106
|
+
attempt++;
|
|
107
|
+
return [3 /*break*/, 1];
|
|
108
|
+
case 7: return [2 /*return*/];
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}); };
|
|
112
|
+
executeRequest = this.retryCount > 0 ? executeWithRetry : function (fn) { return fn(); };
|
|
79
113
|
if (this.debouncer) {
|
|
80
|
-
this.debouncer.debounce(sendRequest);
|
|
114
|
+
return [2 /*return*/, this.debouncer.debounce(function () { return executeRequest(sendRequest); })];
|
|
81
115
|
}
|
|
82
116
|
else {
|
|
83
|
-
return [2 /*return*/, sendRequest
|
|
117
|
+
return [2 /*return*/, executeRequest(sendRequest)];
|
|
84
118
|
}
|
|
85
119
|
return [2 /*return*/];
|
|
86
120
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceClient.js","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,oDAAmD;AAEnD,uDAAsD;AAEtD;IAGE,uBACU,WAAmB,EACnB,aAAqB,EACrB,SAAqB;
|
|
1
|
+
{"version":3,"file":"ServiceClient.js","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -80,7 +80,7 @@ var ServiceRouter = /** @class */ (function () {
|
|
|
80
80
|
if (!serviceExecutor) {
|
|
81
81
|
error = "Unable to find service matching path: ".concat(request.path);
|
|
82
82
|
this.logger.error({ message: error });
|
|
83
|
-
response.send({ error: error });
|
|
83
|
+
response.status(404).send({ error: error });
|
|
84
84
|
return [2 /*return*/];
|
|
85
85
|
}
|
|
86
86
|
_a.label = 1;
|
|
@@ -98,7 +98,7 @@ var ServiceRouter = /** @class */ (function () {
|
|
|
98
98
|
this.logger.error({ error: error_1 });
|
|
99
99
|
errorMessage = 'Internal server error';
|
|
100
100
|
}
|
|
101
|
-
response.send({ error: errorMessage });
|
|
101
|
+
response.status(400).send({ error: errorMessage });
|
|
102
102
|
return [3 /*break*/, 4];
|
|
103
103
|
case 4: return [2 /*return*/];
|
|
104
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceRouter.js","sourceRoot":"","sources":["../../src/ServiceRouter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,oDAAoE;AACpE,qDAAkE;AAClE,wCAA+C;AAC/C,4CAA2C;AAE3C,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAK,KAAsB,CAAC,IAAI,KAAK,cAAc,CAClH,CAAC;AACJ,CAAC;AAED;IAAA;QACU,WAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QAE7D,SAAI,GAAG,WAAW,CAAC;QACnB,WAAM,GAAW,MAAM,CAAC;IA4C1B,CAAC;IA1CS,6CAAqB,GAA7B;QACE,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE;YAC5B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;YAC7B,IAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,6BAAgB,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACxG,KAA0B,UAAY,EAAZ,6BAAY,EAAZ,0BAAY,EAAZ,IAAY,EAAE;gBAAnC,IAAM,WAAW,qBAAA;gBACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,2BAAoB,WAAW,CAAC,aAAa,CAAE,EAAE,CAAC,CAAC;gBAC/E,IAAI,CAAC,IAAA,mBAAY,EAAC,WAAW,EAAE,sBAAS,CAAC,EAAE;oBACzC,SAAS;iBACV;gBAED,IAAM,OAAO,GAAG,6BAAgB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAU,WAAW,CAAC,aAAa,CAAC,CAAC;gBAClF,KAAqB,UAAkC,EAAlC,KAAC,WAAyB,CAAC,OAAO,EAAlC,cAAkC,EAAlC,IAAkC,EAAE;oBAApD,IAAM,MAAM,SAAA;oBACf,IAAM,WAAW,GAAG,mBAAY,WAAW,CAAC,aAAa,cAAI,MAAM,CAAC,IAAI,CAAE,CAAC;oBAC3E,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,GAAG,IAAI,iCAAe,CAAC,OAAO,EAAE,WAAwB,EAAE,MAAM,CAAC,CAAC;iBACvG;aACF;SACF;QAED,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAEK,iCAAS,GAAf,UAAgB,OAAY,EAAE,QAAa;;;;;;wBACnC,eAAe,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;wBACnE,IAAI,CAAC,eAAe,EAAE;4BACd,KAAK,GAAG,gDAAyC,OAAO,CAAC,IAAI,CAAE,CAAC;4BACtE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;4BACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,OAAA,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ServiceRouter.js","sourceRoot":"","sources":["../../src/ServiceRouter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/service",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Service api",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
},
|
|
44
44
|
"main": "./dist/generated/index.js",
|
|
45
45
|
"types": "./dist/generated/index.d.ts",
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "3de027b69d80594ca40d10c2069b6ec1ef119f89"
|
|
47
47
|
}
|
package/src/Service.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SerializableFunction, NotFunction } from '@proteinjs/serializer';
|
|
2
2
|
import { Loadable, SourceRepository } from '@proteinjs/reflection';
|
|
3
3
|
import { ServiceClient } from './ServiceClient';
|
|
4
|
-
import { Debouncer } from '@proteinjs/util';
|
|
4
|
+
import { Debouncer, isInstanceOf } from '@proteinjs/util';
|
|
5
5
|
|
|
6
6
|
type RemoveIndex<T> = {
|
|
7
7
|
[K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K];
|
|
@@ -18,6 +18,14 @@ type RetryConfig<T extends Service> = {
|
|
|
18
18
|
[K in KeysWithoutService<T>]?: number;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
interface DebounceConfig {
|
|
22
|
+
waitTime: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type MethodDebounceConfig<T extends Service> = {
|
|
26
|
+
[K in KeysWithoutService<T>]?: DebounceConfig;
|
|
27
|
+
};
|
|
28
|
+
|
|
21
29
|
export interface Service extends Loadable {
|
|
22
30
|
serviceMetadata?: {
|
|
23
31
|
auth?: {
|
|
@@ -41,16 +49,27 @@ export interface Service extends Loadable {
|
|
|
41
49
|
[prop: string]: SerializableFunction | NotFunction<any>;
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
const debouncerMap: { [key: string]: Debouncer } = {};
|
|
53
|
+
/** Retrieve an existing debouncer from the map or make a new one.
|
|
54
|
+
* This allows the debouncer instance per method to remain the same across multiple service calls.
|
|
55
|
+
*/
|
|
56
|
+
function getOrCreateDebouncer(methodName: string, waitTime: number): Debouncer {
|
|
57
|
+
if (!debouncerMap[methodName]) {
|
|
58
|
+
debouncerMap[methodName] = new Debouncer(waitTime);
|
|
59
|
+
}
|
|
60
|
+
return debouncerMap[methodName];
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
/**
|
|
45
64
|
* Create a factory that creates an instance of the Service. The Service instance is a
|
|
46
65
|
* ServiceClient wrapped in the interface's api.
|
|
47
66
|
* @param serviceInterfaceQualifiedName the package-qualified name of the service interface (ie. service-package-name/MyService)
|
|
48
|
-
* @param debouncer
|
|
67
|
+
* @param debouncer pass in either a single debouncer instance or method-specific debounce configurations
|
|
49
68
|
* @returns a function that creates a Service
|
|
50
69
|
*/
|
|
51
70
|
export const serviceFactory = <T extends Service>(
|
|
52
71
|
serviceInterfaceQualifiedName: string,
|
|
53
|
-
debouncer?: Debouncer,
|
|
72
|
+
debouncer?: MethodDebounceConfig<T> | Debouncer,
|
|
54
73
|
retryConfig?: RetryConfig<T>
|
|
55
74
|
): (() => T) => {
|
|
56
75
|
return () => {
|
|
@@ -58,29 +77,27 @@ export const serviceFactory = <T extends Service>(
|
|
|
58
77
|
const serviceInterface = SourceRepository.get().interface(serviceInterfaceQualifiedName);
|
|
59
78
|
for (const method of serviceInterface.methods) {
|
|
60
79
|
const servicePath = `/service/${serviceInterface.qualifiedName}/${method.name}`;
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
if (lastError) {
|
|
78
|
-
throw lastError;
|
|
80
|
+
const methodName = method.name as KeysWithoutService<T>;
|
|
81
|
+
|
|
82
|
+
let retryCount = 0;
|
|
83
|
+
if (retryConfig && methodName in retryConfig) {
|
|
84
|
+
retryCount = retryConfig[methodName]!;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let methodDebouncer: Debouncer | undefined;
|
|
88
|
+
if (debouncer) {
|
|
89
|
+
if (isInstanceOf(debouncer, Debouncer)) {
|
|
90
|
+
methodDebouncer = debouncer as Debouncer;
|
|
91
|
+
} else if (methodName in debouncer) {
|
|
92
|
+
const methodConfig = (debouncer as MethodDebounceConfig<T>)[methodName];
|
|
93
|
+
if (methodConfig) {
|
|
94
|
+
methodDebouncer = getOrCreateDebouncer(methodName as string, methodConfig.waitTime);
|
|
79
95
|
}
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
service[method.name] = serviceClient.send.bind(serviceClient);
|
|
96
|
+
}
|
|
83
97
|
}
|
|
98
|
+
|
|
99
|
+
const serviceClient = new ServiceClient(servicePath, method, methodDebouncer, retryCount);
|
|
100
|
+
service[methodName] = serviceClient.send.bind(serviceClient);
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
return service;
|
package/src/ServiceClient.ts
CHANGED
|
@@ -9,7 +9,8 @@ export class ServiceClient {
|
|
|
9
9
|
constructor(
|
|
10
10
|
private servicePath: string,
|
|
11
11
|
private serviceMethod: Method,
|
|
12
|
-
private debouncer?: Debouncer
|
|
12
|
+
private debouncer?: Debouncer,
|
|
13
|
+
private retryCount: number = 0
|
|
13
14
|
) {}
|
|
14
15
|
|
|
15
16
|
async send(...args: any[]): Promise<any> {
|
|
@@ -31,10 +32,26 @@ export class ServiceClient {
|
|
|
31
32
|
return deserializedReturn;
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
const executeWithRetry = async (fn: () => Promise<any>) => {
|
|
36
|
+
const maxAttempts = 1 + this.retryCount;
|
|
37
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
38
|
+
try {
|
|
39
|
+
return await fn();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (attempt === maxAttempts - 1) {
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
await new Promise((resolve) => setTimeout(resolve, 1000)); // 1 second delay before retry
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const executeRequest = this.retryCount > 0 ? executeWithRetry : (fn: () => Promise<any>) => fn();
|
|
50
|
+
|
|
34
51
|
if (this.debouncer) {
|
|
35
|
-
this.debouncer.debounce(sendRequest);
|
|
52
|
+
return this.debouncer.debounce(() => executeRequest(sendRequest));
|
|
36
53
|
} else {
|
|
37
|
-
return sendRequest
|
|
54
|
+
return executeRequest(sendRequest);
|
|
38
55
|
}
|
|
39
56
|
}
|
|
40
57
|
|
package/src/ServiceRouter.ts
CHANGED
|
@@ -43,7 +43,7 @@ export class ServiceRouter implements Route {
|
|
|
43
43
|
if (!serviceExecutor) {
|
|
44
44
|
const error = `Unable to find service matching path: ${request.path}`;
|
|
45
45
|
this.logger.error({ message: error });
|
|
46
|
-
response.send({ error });
|
|
46
|
+
response.status(404).send({ error });
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -56,7 +56,7 @@ export class ServiceRouter implements Route {
|
|
|
56
56
|
this.logger.error({ error });
|
|
57
57
|
errorMessage = 'Internal server error';
|
|
58
58
|
}
|
|
59
|
-
response.send({ error: errorMessage });
|
|
59
|
+
response.status(400).send({ error: errorMessage });
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|