@nmxjs/api 1.3.0 → 1.3.1
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/dist/ApiRouterCore/utils/RpcExceptionInterceptor.js +13 -6
- package/dist/ApiRouterCore/utils/RpcExceptionInterceptor.js.map +1 -1
- package/dist/ApiService/services/CreateApiService.js +5 -1
- package/dist/ApiService/services/CreateApiService.js.map +1 -1
- package/dist/ApiService/utils/index.d.ts +1 -0
- package/dist/ApiService/utils/index.js +1 -0
- package/dist/ApiService/utils/index.js.map +1 -1
- package/dist/ApiService/utils/parseGrpcError.d.ts +1 -0
- package/dist/ApiService/utils/parseGrpcError.js +25 -0
- package/dist/ApiService/utils/parseGrpcError.js.map +1 -0
- package/package.json +1 -1
- package/tests/ApiRouterCore/utils/RpcExceptionInterceptor.spec.ts +127 -5
- package/tests/ApiService/utils/parseGrpcError.spec.ts +80 -0
|
@@ -36,8 +36,10 @@ let RpcExceptionInterceptor = class RpcExceptionInterceptor {
|
|
|
36
36
|
})}`);
|
|
37
37
|
}
|
|
38
38
|
}), (0, rxjs_1.catchError)(e => {
|
|
39
|
-
const
|
|
40
|
-
const
|
|
39
|
+
const message = typeof (e === null || e === void 0 ? void 0 : e.message) === 'string' ? e.message : String(e);
|
|
40
|
+
const splitResult = message.split(constants_1.endErrorText);
|
|
41
|
+
const isProcessed = splitResult.length > 1;
|
|
42
|
+
const errorMessage = isProcessed ? splitResult[0] : message;
|
|
41
43
|
if (this.debug) {
|
|
42
44
|
common_1.Logger.debug(`Rpc Response: ${(0, utils_1.readableJson)({
|
|
43
45
|
requestId,
|
|
@@ -46,17 +48,22 @@ let RpcExceptionInterceptor = class RpcExceptionInterceptor {
|
|
|
46
48
|
},
|
|
47
49
|
})}`);
|
|
48
50
|
}
|
|
49
|
-
common_1.Logger.error(errorMessage);
|
|
50
|
-
if (this.notifier && !e.silent) {
|
|
51
|
+
common_1.Logger.error(isProcessed ? errorMessage : (e === null || e === void 0 ? void 0 : e.stack) || errorMessage);
|
|
52
|
+
if (this.notifier && !(e === null || e === void 0 ? void 0 : e.silent)) {
|
|
51
53
|
this.notifier.sendError({
|
|
52
54
|
message: errorMessage.split('\n at')[0],
|
|
53
55
|
serviceName: this.serviceName,
|
|
54
56
|
path,
|
|
55
|
-
code: e.code || 'UNKNOWN RPC',
|
|
57
|
+
code: (e === null || e === void 0 ? void 0 : e.code) || 'UNKNOWN RPC',
|
|
56
58
|
params: context.getArgByIndex(0),
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
|
-
|
|
61
|
+
const errorPayload = JSON.stringify({
|
|
62
|
+
message: errorMessage.split('\n at')[0],
|
|
63
|
+
code: e === null || e === void 0 ? void 0 : e.code,
|
|
64
|
+
statusCode: e === null || e === void 0 ? void 0 : e.statusCode,
|
|
65
|
+
});
|
|
66
|
+
throw new microservices_1.RpcException(`${errorPayload}${constants_1.endErrorText}`);
|
|
60
67
|
}));
|
|
61
68
|
}
|
|
62
69
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RpcExceptionInterceptor.js","sourceRoot":"","sources":["../../../src/ApiRouterCore/utils/RpcExceptionInterceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+BAAuC;AACvC,yDAAqD;AACrD,wCAAkD;AAClD,2CAAoG;AACpG,4CAA4C;AAIrC,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAClC,
|
|
1
|
+
{"version":3,"file":"RpcExceptionInterceptor.js","sourceRoot":"","sources":["../../../src/ApiRouterCore/utils/RpcExceptionInterceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+BAAuC;AACvC,yDAAqD;AACrD,wCAAkD;AAClD,2CAAoG;AACpG,4CAA4C;AAIrC,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAClC,YACqB,WAAmB,EACnB,QAAiB,KAAK,EACtB,QAAoB;QAFpB,gBAAW,GAAX,WAAW,CAAQ;QACnB,UAAK,GAAL,KAAK,CAAiB;QACtB,aAAQ,GAAR,QAAQ,CAAY;IACtC,CAAC;IAEG,SAAS,CAAC,OAAyB,EAAE,IAAiB;;QAC3D,MAAM,SAAS,GAAG,IAAA,YAAI,GAAE,CAAC;QACzB,MAAM,IAAI,GAAG,CAAA,MAAA,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,0CAAE,IAAI,MAAI,MAAA,MAAA,MAAA,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,0CAAE,IAAI,0CAAE,OAAO,0CAAE,IAAI,CAAA,CAAC;QAE7F,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CACV,gBAAgB,IAAA,oBAAY,kBAC1B,SAAS,EACT,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,IAC3B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAClE,EAAE,CACL,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CACvB,IAAA,UAAG,EAAC,IAAI,CAAC,EAAE;YACT,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,eAAM,CAAC,KAAK,CACV,iBAAiB,IAAA,oBAAY,EAAC;oBAC5B,SAAS;oBACT,IAAI;iBACL,CAAC,EAAE,CACL,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,EACF,IAAA,iBAAU,EAAC,CAAC,CAAC,EAAE;YACb,MAAM,OAAO,GAAG,OAAO,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,CAAA,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvE,MAAM,WAAW,GAAa,OAAO,CAAC,KAAK,CAAC,wBAAY,CAAC,CAAC;YAC1D,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;YAC3C,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAE5D,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,eAAM,CAAC,KAAK,CACV,iBAAiB,IAAA,oBAAY,EAAC;oBAC5B,SAAS;oBACT,KAAK,EAAE;wBACL,OAAO,EAAE,YAAY;qBACtB;iBACF,CAAC,EAAE,CACL,CAAC;YACJ,CAAC;YACD,eAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,KAAK,KAAI,YAAY,CAAC,CAAC;YACpE,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,MAAM,CAAA,EAAE,CAAC;gBAChC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;oBACtB,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;oBAC1C,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,IAAI;oBACJ,IAAI,EAAE,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,IAAI,KAAI,aAAa;oBAC9B,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;iBACjC,CAAC,CAAC;YACL,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC;gBAClC,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAC1C,IAAI,EAAE,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,IAAI;gBACb,UAAU,EAAE,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,UAAU;aAC1B,CAAC,CAAC;YAEH,MAAM,IAAI,4BAAY,CAAC,GAAG,YAAY,GAAG,wBAAY,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAA;AApEY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;;GACA,uBAAuB,CAoEnC"}
|
|
@@ -55,15 +55,19 @@ let CreateApiService = class CreateApiService {
|
|
|
55
55
|
const getData = async () => {
|
|
56
56
|
const payload = currentService[methodName](resultRequestData);
|
|
57
57
|
const result = await ((0, rxjs_1.isObservable)(payload) ? (0, rxjs_1.lastValueFrom)(payload) : payload).catch(e => {
|
|
58
|
+
var _a;
|
|
58
59
|
if (requestData.skipError || methodOptions.skipError) {
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
if (typeof e === 'string') {
|
|
62
63
|
throw new Error(e);
|
|
63
64
|
}
|
|
64
|
-
if (e.message.includes('Empty response. There are no subscribers listening to that message')) {
|
|
65
|
+
if ((_a = e.message) === null || _a === void 0 ? void 0 : _a.includes('Empty response. There are no subscribers listening to that message')) {
|
|
65
66
|
throw new errors_1.ServiceNotAvailableError(serviceName, methodName);
|
|
66
67
|
}
|
|
68
|
+
if (e.details) {
|
|
69
|
+
throw (0, utils_1.parseGrpcError)(e);
|
|
70
|
+
}
|
|
67
71
|
throw e;
|
|
68
72
|
});
|
|
69
73
|
return (0, utils_1.transformParseJson)(`${route}.response`, result);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateApiService.js","sourceRoot":"","sources":["../../../src/ApiService/services/CreateApiService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wCAAwC;AACxC,uCAAuC;AACvC,2CAAoD;AACpD,0CAAmD;AACnD,+BAAmD;AACnD,0CAAyD;AACzD,4CAAoE;AAEpE,mEAAgE;AAChE,
|
|
1
|
+
{"version":3,"file":"CreateApiService.js","sourceRoot":"","sources":["../../../src/ApiService/services/CreateApiService.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./getJsonFieldsKeys"), exports);
|
|
18
18
|
__exportStar(require("./getQueryMutationByName"), exports);
|
|
19
|
+
__exportStar(require("./parseGrpcError"), exports);
|
|
19
20
|
__exportStar(require("./transformParseJson"), exports);
|
|
20
21
|
__exportStar(require("./transformStringifyJson"), exports);
|
|
21
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ApiService/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,2DAAyC;AACzC,uDAAqC;AACrC,2DAAyC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ApiService/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,2DAAyC;AACzC,mDAAiC;AACjC,uDAAqC;AACrC,2DAAyC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseGrpcError(e: any): Error;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseGrpcError = void 0;
|
|
4
|
+
const constants_1 = require("../../ApiRouterCore/constants");
|
|
5
|
+
function parseGrpcError(e) {
|
|
6
|
+
const details = e.details;
|
|
7
|
+
const cleanDetails = details.split(constants_1.endErrorText)[0];
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(cleanDetails);
|
|
10
|
+
const error = new Error(parsed.message || cleanDetails);
|
|
11
|
+
if (parsed.code)
|
|
12
|
+
error.code = parsed.code;
|
|
13
|
+
if (parsed.statusCode)
|
|
14
|
+
error.statusCode = parsed.statusCode;
|
|
15
|
+
return error;
|
|
16
|
+
}
|
|
17
|
+
catch (parseError) {
|
|
18
|
+
if (parseError instanceof SyntaxError) {
|
|
19
|
+
return new Error(cleanDetails);
|
|
20
|
+
}
|
|
21
|
+
throw parseError;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.parseGrpcError = parseGrpcError;
|
|
25
|
+
//# sourceMappingURL=parseGrpcError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseGrpcError.js","sourceRoot":"","sources":["../../../src/ApiService/utils/parseGrpcError.ts"],"names":[],"mappings":";;;AAAA,6DAA6D;AAE7D,SAAgB,cAAc,CAAC,CAAM;IACnC,MAAM,OAAO,GAAW,CAAC,CAAC,OAAO,CAAC;IAClC,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,wBAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAEpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxC,MAAM,KAAK,GAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,IAAI,YAAY,CAAC,CAAC;QAC7D,IAAI,MAAM,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAC1C,IAAI,MAAM,CAAC,UAAU;YAAE,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAC5D,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,UAAU,EAAE,CAAC;QACpB,IAAI,UAAU,YAAY,WAAW,EAAE,CAAC;YACtC,OAAO,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,UAAU,CAAC;IACnB,CAAC;AACH,CAAC;AAhBD,wCAgBC"}
|
package/package.json
CHANGED
|
@@ -64,7 +64,7 @@ describe('RpcExceptionInterceptor', () => {
|
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it('should throw RpcException on error', done => {
|
|
67
|
+
it('should throw RpcException with JSON payload on error', done => {
|
|
68
68
|
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
69
69
|
const ctx = createContext({ id: 1 });
|
|
70
70
|
const error = new Error('Something went wrong');
|
|
@@ -72,8 +72,30 @@ describe('RpcExceptionInterceptor', () => {
|
|
|
72
72
|
|
|
73
73
|
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
74
74
|
error: err => {
|
|
75
|
-
expect(err.message).toContain('Something went wrong');
|
|
76
75
|
expect(err.message).toContain(endErrorText);
|
|
76
|
+
const payload = err.message.split(endErrorText)[0];
|
|
77
|
+
const parsed = JSON.parse(payload);
|
|
78
|
+
expect(parsed.message).toBe('Something went wrong');
|
|
79
|
+
done();
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should include code and statusCode in JSON payload', done => {
|
|
85
|
+
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
86
|
+
const ctx = createContext({});
|
|
87
|
+
const error: any = new Error('Validation failed');
|
|
88
|
+
error.code = 'VALIDATION_ERROR';
|
|
89
|
+
error.statusCode = 400;
|
|
90
|
+
const next = createErrorNext(error);
|
|
91
|
+
|
|
92
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
93
|
+
error: err => {
|
|
94
|
+
const payload = err.message.split(endErrorText)[0];
|
|
95
|
+
const parsed = JSON.parse(payload);
|
|
96
|
+
expect(parsed.message).toBe('Validation failed');
|
|
97
|
+
expect(parsed.code).toBe('VALIDATION_ERROR');
|
|
98
|
+
expect(parsed.statusCode).toBe(400);
|
|
77
99
|
done();
|
|
78
100
|
},
|
|
79
101
|
});
|
|
@@ -93,15 +115,49 @@ describe('RpcExceptionInterceptor', () => {
|
|
|
93
115
|
});
|
|
94
116
|
});
|
|
95
117
|
|
|
96
|
-
it('should handle error with endErrorText
|
|
118
|
+
it('should handle already-processed error with endErrorText', done => {
|
|
97
119
|
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
98
120
|
const ctx = createContext({});
|
|
99
|
-
const
|
|
121
|
+
const originalPayload = JSON.stringify({ message: 'Original error', code: 'ERR' });
|
|
122
|
+
const error = new Error(`${originalPayload}${endErrorText}`);
|
|
100
123
|
const next = createErrorNext(error);
|
|
101
124
|
|
|
102
125
|
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
103
126
|
error: err => {
|
|
104
|
-
|
|
127
|
+
const payload = err.message.split(endErrorText)[0];
|
|
128
|
+
const parsed = JSON.parse(payload);
|
|
129
|
+
expect(parsed.message).toContain('Original error');
|
|
130
|
+
done();
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should log stack for fresh errors', done => {
|
|
136
|
+
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
137
|
+
const ctx = createContext({});
|
|
138
|
+
const error = new Error('Fresh error');
|
|
139
|
+
const next = createErrorNext(error);
|
|
140
|
+
|
|
141
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
142
|
+
error: () => {
|
|
143
|
+
const loggedMessage = (Logger.error as jest.Mock).mock.calls[0][0];
|
|
144
|
+
expect(loggedMessage).toContain('Fresh error');
|
|
145
|
+
done();
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should log only message for processed errors', done => {
|
|
151
|
+
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
152
|
+
const ctx = createContext({});
|
|
153
|
+
const error = new Error(`Processed message${endErrorText}`);
|
|
154
|
+
const next = createErrorNext(error);
|
|
155
|
+
|
|
156
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
157
|
+
error: () => {
|
|
158
|
+
const loggedMessage = (Logger.error as jest.Mock).mock.calls[0][0];
|
|
159
|
+
expect(loggedMessage).toBe('Processed message');
|
|
160
|
+
expect(loggedMessage).not.toContain('at ');
|
|
105
161
|
done();
|
|
106
162
|
},
|
|
107
163
|
});
|
|
@@ -175,4 +231,70 @@ describe('RpcExceptionInterceptor', () => {
|
|
|
175
231
|
done();
|
|
176
232
|
});
|
|
177
233
|
});
|
|
234
|
+
|
|
235
|
+
it('should handle non-Error thrown value (e.g. string)', done => {
|
|
236
|
+
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
237
|
+
const ctx = createContext({});
|
|
238
|
+
const next = {
|
|
239
|
+
handle: () => throwError(() => 'string error'),
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
243
|
+
error: err => {
|
|
244
|
+
const payload = err.message.split(endErrorText)[0];
|
|
245
|
+
const parsed = JSON.parse(payload);
|
|
246
|
+
expect(parsed.message).toBe('string error');
|
|
247
|
+
done();
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should handle error without message property', done => {
|
|
253
|
+
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
254
|
+
const ctx = createContext({});
|
|
255
|
+
const next = {
|
|
256
|
+
handle: () => throwError(() => ({ code: 'SOME_CODE' })),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
260
|
+
error: err => {
|
|
261
|
+
const payload = err.message.split(endErrorText)[0];
|
|
262
|
+
const parsed = JSON.parse(payload);
|
|
263
|
+
expect(parsed.code).toBe('SOME_CODE');
|
|
264
|
+
done();
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should handle null thrown value', done => {
|
|
270
|
+
const interceptor = new RpcExceptionInterceptor('test-service');
|
|
271
|
+
const ctx = createContext({});
|
|
272
|
+
const next = {
|
|
273
|
+
handle: () => throwError(() => null),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
277
|
+
error: err => {
|
|
278
|
+
expect(err.message).toContain(endErrorText);
|
|
279
|
+
done();
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should strip stack trace from notifier message', done => {
|
|
285
|
+
const notifier = { sendError: jest.fn() };
|
|
286
|
+
const interceptor = new RpcExceptionInterceptor('test-service', false, notifier as any);
|
|
287
|
+
const ctx = createContext({}, { path: '/test' });
|
|
288
|
+
const error = new Error('Error message');
|
|
289
|
+
const next = createErrorNext(error);
|
|
290
|
+
|
|
291
|
+
interceptor.intercept(ctx as any, next as any).subscribe({
|
|
292
|
+
error: () => {
|
|
293
|
+
const sendErrorCall = notifier.sendError.mock.calls[0][0];
|
|
294
|
+
expect(sendErrorCall.message).toBe('Error message');
|
|
295
|
+
expect(sendErrorCall.message).not.toContain('\n at');
|
|
296
|
+
done();
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
});
|
|
178
300
|
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { parseGrpcError } from '../../../src/ApiService/utils/parseGrpcError';
|
|
2
|
+
import { endErrorText } from '../../../src/ApiRouterCore/constants/endErrorText';
|
|
3
|
+
|
|
4
|
+
describe('parseGrpcError', () => {
|
|
5
|
+
it('should parse JSON details with message, code and statusCode', () => {
|
|
6
|
+
const payload = JSON.stringify({ message: 'Validation failed', code: 'VALIDATION_ERROR', statusCode: 400 });
|
|
7
|
+
const grpcError = { details: `${payload}${endErrorText}` };
|
|
8
|
+
const result: any = parseGrpcError(grpcError);
|
|
9
|
+
|
|
10
|
+
expect(result.message).toBe('Validation failed');
|
|
11
|
+
expect(result.code).toBe('VALIDATION_ERROR');
|
|
12
|
+
expect(result.statusCode).toBe(400);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should parse JSON details with only message', () => {
|
|
16
|
+
const payload = JSON.stringify({ message: 'Something broke' });
|
|
17
|
+
const grpcError = { details: `${payload}${endErrorText}` };
|
|
18
|
+
const result = parseGrpcError(grpcError);
|
|
19
|
+
|
|
20
|
+
expect(result.message).toBe('Something broke');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle plain text details (not JSON)', () => {
|
|
24
|
+
const grpcError = { details: `Plain error text${endErrorText}` };
|
|
25
|
+
const result = parseGrpcError(grpcError);
|
|
26
|
+
|
|
27
|
+
expect(result.message).toBe('Plain error text');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle details without endErrorText marker', () => {
|
|
31
|
+
const payload = JSON.stringify({ message: 'Error without marker', code: 'ERR' });
|
|
32
|
+
const grpcError = { details: payload };
|
|
33
|
+
const result: any = parseGrpcError(grpcError);
|
|
34
|
+
|
|
35
|
+
expect(result.message).toBe('Error without marker');
|
|
36
|
+
expect(result.code).toBe('ERR');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should fallback to details as message when JSON has no message field', () => {
|
|
40
|
+
const payload = JSON.stringify({ code: 'UNKNOWN' });
|
|
41
|
+
const grpcError = { details: `${payload}${endErrorText}` };
|
|
42
|
+
const result = parseGrpcError(grpcError);
|
|
43
|
+
|
|
44
|
+
expect(result.message).toBe(payload);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should not set code if not present in parsed JSON', () => {
|
|
48
|
+
const payload = JSON.stringify({ message: 'No code error' });
|
|
49
|
+
const grpcError = { details: `${payload}${endErrorText}` };
|
|
50
|
+
const result: any = parseGrpcError(grpcError);
|
|
51
|
+
|
|
52
|
+
expect(result.message).toBe('No code error');
|
|
53
|
+
expect(result.code).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should not set statusCode if not present in parsed JSON', () => {
|
|
57
|
+
const payload = JSON.stringify({ message: 'No status error' });
|
|
58
|
+
const grpcError = { details: `${payload}${endErrorText}` };
|
|
59
|
+
const result: any = parseGrpcError(grpcError);
|
|
60
|
+
|
|
61
|
+
expect(result.statusCode).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should handle cascade errors (multiple endErrorText markers)', () => {
|
|
65
|
+
const payload = JSON.stringify({ message: 'Original cascade error', code: 'CASCADE' });
|
|
66
|
+
const grpcError = { details: `${payload}${endErrorText}${endErrorText}` };
|
|
67
|
+
const result: any = parseGrpcError(grpcError);
|
|
68
|
+
|
|
69
|
+
expect(result.message).toBe('Original cascade error');
|
|
70
|
+
expect(result.code).toBe('CASCADE');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should return Error instance', () => {
|
|
74
|
+
const payload = JSON.stringify({ message: 'test' });
|
|
75
|
+
const grpcError = { details: payload };
|
|
76
|
+
const result = parseGrpcError(grpcError);
|
|
77
|
+
|
|
78
|
+
expect(result).toBeInstanceOf(Error);
|
|
79
|
+
});
|
|
80
|
+
});
|