@gravity-ui/gateway 1.0.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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +269 -0
  3. package/bin/patch.js +13 -0
  4. package/build/components/grpc.d.ts +24 -0
  5. package/build/components/grpc.js +664 -0
  6. package/build/components/mixed.d.ts +11 -0
  7. package/build/components/mixed.js +66 -0
  8. package/build/components/rest.d.ts +7 -0
  9. package/build/components/rest.js +291 -0
  10. package/build/constants.d.ts +46 -0
  11. package/build/constants.js +101 -0
  12. package/build/index.d.ts +11 -0
  13. package/build/index.js +285 -0
  14. package/build/models/common.d.ts +241 -0
  15. package/build/models/common.js +8 -0
  16. package/build/models/context.d.ts +22 -0
  17. package/build/models/context.js +2 -0
  18. package/build/models/error.d.ts +12 -0
  19. package/build/models/error.js +2 -0
  20. package/build/utils/axios.d.ts +2 -0
  21. package/build/utils/axios.js +24 -0
  22. package/build/utils/common.d.ts +15 -0
  23. package/build/utils/common.js +50 -0
  24. package/build/utils/create-context-api.d.ts +4 -0
  25. package/build/utils/create-context-api.js +45 -0
  26. package/build/utils/grpc-reflection.d.ts +22 -0
  27. package/build/utils/grpc-reflection.js +56 -0
  28. package/build/utils/grpc.d.ts +15 -0
  29. package/build/utils/grpc.js +55 -0
  30. package/build/utils/overrideEndpoints/index.d.ts +2 -0
  31. package/build/utils/overrideEndpoints/index.js +4 -0
  32. package/build/utils/overrideEndpoints/overrideEndpoints.d.ts +17 -0
  33. package/build/utils/overrideEndpoints/overrideEndpoints.js +103 -0
  34. package/build/utils/parse-error.d.ts +32 -0
  35. package/build/utils/parse-error.js +224 -0
  36. package/build/utils/redact-sensitive-headers.d.ts +4 -0
  37. package/build/utils/redact-sensitive-headers.js +16 -0
  38. package/build/utils/typed-api.d.ts +2 -0
  39. package/build/utils/typed-api.js +7 -0
  40. package/build/utils/validate.d.ts +4 -0
  41. package/build/utils/validate.js +56 -0
  42. package/package.json +91 -0
  43. package/patches/grpc-reflection-js+0.1.2.patch +87 -0
  44. package/patches/protobufjs+6.11.4.patch +121 -0
  45. package/proto/google/api/annotations.proto +31 -0
  46. package/proto/google/api/http.proto +291 -0
  47. package/proto/google/protobuf/any.proto +158 -0
  48. package/proto/google/protobuf/api.proto +208 -0
  49. package/proto/google/protobuf/compiler/plugin.proto +183 -0
  50. package/proto/google/protobuf/descriptor.proto +909 -0
  51. package/proto/google/protobuf/duration.proto +116 -0
  52. package/proto/google/protobuf/empty.proto +52 -0
  53. package/proto/google/protobuf/field_mask.proto +245 -0
  54. package/proto/google/protobuf/source_context.proto +48 -0
  55. package/proto/google/protobuf/struct.proto +95 -0
  56. package/proto/google/protobuf/timestamp.proto +147 -0
  57. package/proto/google/protobuf/type.proto +187 -0
  58. package/proto/google/protobuf/wrappers.proto +123 -0
  59. package/proto/google/rpc/README.md +5 -0
  60. package/proto/google/rpc/code.proto +186 -0
  61. package/proto/google/rpc/error_details.proto +200 -0
  62. package/proto/google/rpc/status.proto +92 -0
  63. package/proto/google/type/dayofweek.proto +51 -0
  64. package/proto/google/type/timeofday.proto +45 -0
@@ -0,0 +1,664 @@
1
+ "use strict";
2
+ /* eslint-disable camelcase */
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.getCredentialsMap = exports.createRoot = void 0;
31
+ const fs_1 = __importDefault(require("fs"));
32
+ const path_1 = __importDefault(require("path"));
33
+ const grpc = __importStar(require("@grpc/grpc-js"));
34
+ const protoLoader = __importStar(require("@grpc/proto-loader"));
35
+ const lodash_1 = __importDefault(require("lodash"));
36
+ const object_sizeof_1 = __importDefault(require("object-sizeof"));
37
+ const protobufjs = __importStar(require("protobufjs"));
38
+ const uuid_1 = require("uuid");
39
+ const constants_1 = require("../constants");
40
+ const common_1 = require("../models/common");
41
+ const common_2 = require("../utils/common");
42
+ const grpc_1 = require("../utils/grpc");
43
+ const grpc_reflection_1 = require("../utils/grpc-reflection");
44
+ const parse_error_1 = require("../utils/parse-error");
45
+ const redact_sensitive_headers_1 = require("../utils/redact-sensitive-headers");
46
+ const validate_1 = require("../utils/validate");
47
+ const reflectLoaderOptions = {
48
+ longs: String,
49
+ enums: String,
50
+ defaults: true,
51
+ oneofs: true,
52
+ };
53
+ const grpcLoaderOptions = Object.assign(Object.assign({}, reflectLoaderOptions), { includeDirs: [path_1.default.join(__dirname, '../../proto')] });
54
+ function createRoot(includeGrpcPaths) {
55
+ const root = new protobufjs.Root();
56
+ root.loadSync(path_1.default.resolve(__dirname, '../../proto/google/rpc/code.proto'));
57
+ root.loadSync(path_1.default.resolve(__dirname, '../../proto/google/rpc/error_details.proto'));
58
+ root.loadSync(path_1.default.resolve(__dirname, '../../proto/google/rpc/status.proto'));
59
+ // Load well-known internal protobufjs types
60
+ root.loadSync('google/protobuf/struct.proto');
61
+ root.loadSync('google/protobuf/wrappers.proto');
62
+ addIncludePathResolver(root, includeGrpcPaths);
63
+ return root;
64
+ }
65
+ exports.createRoot = createRoot;
66
+ function addIncludePathResolver(root, includeGrpcPaths) {
67
+ grpcLoaderOptions.includeDirs = [...grpcLoaderOptions.includeDirs, ...(includeGrpcPaths !== null && includeGrpcPaths !== void 0 ? includeGrpcPaths : [])];
68
+ const { includeDirs } = grpcLoaderOptions;
69
+ const originalResolvePath = root.resolvePath;
70
+ root.resolvePath = function (origin, target) {
71
+ if (target in protobufjs.common || path_1.default.isAbsolute(target)) {
72
+ return target;
73
+ }
74
+ for (let i = 0; i < includeDirs.length; i++) {
75
+ const directory = includeDirs[i];
76
+ const fullPath = path_1.default.join(directory, target);
77
+ try {
78
+ fs_1.default.accessSync(fullPath, fs_1.default.constants.R_OK);
79
+ return fullPath;
80
+ }
81
+ catch (err) {
82
+ continue;
83
+ }
84
+ }
85
+ return originalResolvePath(origin, target);
86
+ };
87
+ }
88
+ function getCredentialsMap(caCertificatePath) {
89
+ let certificate;
90
+ if (caCertificatePath && fs_1.default.existsSync(caCertificatePath)) {
91
+ certificate = fs_1.default.readFileSync(caCertificatePath);
92
+ }
93
+ return {
94
+ secure: grpc.ChannelCredentials.createSsl(certificate),
95
+ secureWithoutRootCert: grpc.ChannelCredentials.createSsl(),
96
+ insecure: grpc.ChannelCredentials.createInsecure(),
97
+ };
98
+ }
99
+ exports.getCredentialsMap = getCredentialsMap;
100
+ function decodeResponse(response, packageRoot, ctx, encodedFields = [], ErrorConctructor) {
101
+ const systemFields = ['metadata', 'response', 'error.details'];
102
+ [...systemFields, ...encodedFields].forEach((fieldName) => {
103
+ try {
104
+ const traverseRegExp = /\.\*$/;
105
+ const needTraverse = traverseRegExp.test(fieldName);
106
+ const parsedFieldName = needTraverse
107
+ ? fieldName.replace(traverseRegExp, '')
108
+ : fieldName;
109
+ const fieldValue = lodash_1.default.get(response, parsedFieldName);
110
+ if (fieldValue) {
111
+ lodash_1.default.set(response, parsedFieldName, needTraverse
112
+ ? (0, grpc_1.traverseAnyMessage)(packageRoot, fieldValue)
113
+ : (0, grpc_1.decodeAnyMessageRecursively)(packageRoot, fieldValue));
114
+ }
115
+ }
116
+ catch (error) {
117
+ (0, common_2.handleError)(ErrorConctructor, error, ctx, 'Message decoding failed', { fieldName });
118
+ }
119
+ });
120
+ }
121
+ function createMetadata({ options, actionConfig, config, params, serviceName, ctx, }) {
122
+ var _a;
123
+ const { headers, requestId, authArgs } = actionConfig;
124
+ const proxyHeaders = [...constants_1.DEFAULT_PROXY_HEADERS];
125
+ let metadata = {
126
+ 'x-request-id': requestId,
127
+ 'accept-language': headers[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru,
128
+ };
129
+ if (typeof options.proxyHeaders === 'function') {
130
+ Object.assign(metadata, options.proxyHeaders(Object.assign({}, headers), 'grpc'));
131
+ }
132
+ else if (Array.isArray(options.proxyHeaders)) {
133
+ proxyHeaders.push(...options.proxyHeaders);
134
+ }
135
+ if (typeof config.proxyHeaders === 'function') {
136
+ Object.assign(metadata, config.proxyHeaders(Object.assign({}, headers), 'grpc'));
137
+ }
138
+ else if (Array.isArray(config.proxyHeaders)) {
139
+ proxyHeaders.push(...config.proxyHeaders);
140
+ }
141
+ for (const headerName of proxyHeaders) {
142
+ if (metadata[headerName] === undefined) {
143
+ metadata[headerName] = headers[headerName];
144
+ }
145
+ }
146
+ const authHeaders = ((_a = config.getAuthHeaders) !== null && _a !== void 0 ? _a : options.getAuthHeaders)({
147
+ actionType: 'grpc',
148
+ serviceName,
149
+ requestHeaders: headers,
150
+ authArgs,
151
+ });
152
+ Object.assign(metadata, authHeaders);
153
+ if (config.idempotency) {
154
+ const idempotencyKey = headers['idempotency-key'] || (0, uuid_1.v4)();
155
+ Object.assign(metadata, { 'idempotency-key': idempotencyKey });
156
+ }
157
+ const { headers: actionHeaders = null } = params !== null && params !== void 0 ? params : {};
158
+ if (actionHeaders) {
159
+ Object.keys(actionHeaders).forEach((key) => {
160
+ metadata[key] = actionHeaders[key];
161
+ });
162
+ }
163
+ metadata = lodash_1.default.omitBy(Object.assign(Object.assign({}, ctx.getMetadata()), metadata), lodash_1.default.isUndefined);
164
+ const serviceMetadata = new grpc.Metadata();
165
+ Object.keys(metadata).forEach((key) => {
166
+ if (key) {
167
+ serviceMetadata.add(key, metadata[key]);
168
+ }
169
+ });
170
+ return serviceMetadata;
171
+ }
172
+ function createActionEndpoint(endpointData) {
173
+ return (0, common_2.isExtendedActionEndpoint)(endpointData) ? endpointData.path : endpointData;
174
+ }
175
+ const packageObjectsMap = new Map();
176
+ const serviceInstancesMap = {};
177
+ const reflectionServiceInstancesMap = {};
178
+ function clearInstancesCache(service, instancesMap, cachePath, closeTimeout, ctx) {
179
+ const cachedService = lodash_1.default.get(instancesMap, cachePath);
180
+ if (cachedService !== service) {
181
+ return;
182
+ }
183
+ // Remove cached service instance
184
+ lodash_1.default.unset(instancesMap, cachePath);
185
+ // Try to close service connection (prevent memory leak)
186
+ // Bug in node >= 18.15.0 https://github.com/grpc/grpc-node/issues/2091
187
+ // use setTimeout
188
+ Promise.resolve(cachedService).then((client) => {
189
+ setTimeout(() => {
190
+ var _a;
191
+ try {
192
+ (_a = client === null || client === void 0 ? void 0 : client.close) === null || _a === void 0 ? void 0 : _a.call(client);
193
+ }
194
+ catch (error) {
195
+ ctx.logError('Failed to close connection during clearing instances cache', error, {
196
+ cachePath,
197
+ });
198
+ }
199
+ }, closeTimeout);
200
+ });
201
+ }
202
+ function getChannelCredential(config, endpointData, credentials) {
203
+ let endpointInsecure;
204
+ let endpointSecureWithoutRootCert;
205
+ if ((0, common_2.isExtendedGrpcActionEndpoint)(endpointData)) {
206
+ endpointInsecure = endpointData === null || endpointData === void 0 ? void 0 : endpointData.insecure;
207
+ endpointSecureWithoutRootCert = endpointData === null || endpointData === void 0 ? void 0 : endpointData.secureWithoutRootCert;
208
+ }
209
+ const isInsecure = config.insecure || endpointInsecure;
210
+ const isSecureWithoutRootCert = config.secureWithoutRootCert || endpointSecureWithoutRootCert;
211
+ let creds = credentials.secure;
212
+ if (isInsecure) {
213
+ creds = credentials.insecure;
214
+ }
215
+ else if (isSecureWithoutRootCert) {
216
+ creds = credentials.secureWithoutRootCert;
217
+ }
218
+ return creds;
219
+ }
220
+ const reflectionCacheStatusMap = {};
221
+ function needRefreshCache(actionEndpoint, protoKey, reflectionRefreshSec) {
222
+ const cacheStatus = lodash_1.default.get(reflectionCacheStatusMap, [protoKey, actionEndpoint]);
223
+ if (!cacheStatus) {
224
+ lodash_1.default.set(reflectionCacheStatusMap, [protoKey, actionEndpoint], {
225
+ time: Date.now() / 1000,
226
+ isRefresh: false,
227
+ });
228
+ return false;
229
+ }
230
+ if (cacheStatus.isRefresh) {
231
+ return false;
232
+ }
233
+ return cacheStatus.time + reflectionRefreshSec < Date.now() / 1000;
234
+ }
235
+ async function refreshCache(actionEndpoint, config, endpointData, grpcOptions, credentials) {
236
+ const cacheStatus = lodash_1.default.get(reflectionCacheStatusMap, [config.protoKey, actionEndpoint]);
237
+ if (!cacheStatus) {
238
+ return;
239
+ }
240
+ lodash_1.default.set(reflectionCacheStatusMap, [config.protoKey, actionEndpoint], Object.assign(Object.assign({}, cacheStatus), { isRefresh: true }));
241
+ const res = await getServiceInstanceReflect(config, endpointData, grpcOptions, credentials, true);
242
+ lodash_1.default.set(reflectionServiceInstancesMap, [config.protoKey, actionEndpoint], Promise.resolve(res));
243
+ lodash_1.default.set(reflectionCacheStatusMap, [config.protoKey, actionEndpoint], {
244
+ time: Date.now() / 1000,
245
+ isRefresh: false,
246
+ });
247
+ }
248
+ function getServiceInstanceReflectCached(config, endpointData, grpcOptions, credentials) {
249
+ if (config.reflection === common_1.GrpcReflection.OnEveryRequest) {
250
+ return getServiceInstanceReflect(config, endpointData, grpcOptions, credentials);
251
+ }
252
+ const actionEndpoint = createActionEndpoint(endpointData);
253
+ const cacheKey = [config.protoKey, actionEndpoint];
254
+ const cachedServiceInstance = lodash_1.default.get(reflectionServiceInstancesMap, cacheKey);
255
+ if (cachedServiceInstance) {
256
+ if (config.reflectionRefreshSec &&
257
+ needRefreshCache(actionEndpoint, config.protoKey, config.reflectionRefreshSec)) {
258
+ refreshCache(actionEndpoint, config, endpointData, grpcOptions, credentials);
259
+ }
260
+ return cachedServiceInstance;
261
+ }
262
+ const service = getServiceInstanceReflect(config, endpointData, grpcOptions, credentials);
263
+ lodash_1.default.set(reflectionServiceInstancesMap, cacheKey, service);
264
+ return service;
265
+ }
266
+ async function getServiceInstanceReflect(config, endpointData, grpcOptions, credentials, isRefreshCache) {
267
+ const actionEndpoint = createActionEndpoint(endpointData);
268
+ const endpointInsecure = (0, common_2.isExtendedGrpcActionEndpoint)(endpointData)
269
+ ? endpointData === null || endpointData === void 0 ? void 0 : endpointData.insecure
270
+ : undefined;
271
+ const isInsecure = config.insecure || endpointInsecure;
272
+ const creds = isInsecure ? credentials.insecure : credentials.secure;
273
+ let loadedRoot;
274
+ if (config.reflection === common_1.GrpcReflection.OnEveryRequest || isRefreshCache) {
275
+ loadedRoot = await (0, grpc_reflection_1.getReflectionRoot)(actionEndpoint, config.protoKey, creds, isRefreshCache);
276
+ }
277
+ else {
278
+ loadedRoot = await (0, grpc_reflection_1.getCachedReflectionRoot)(actionEndpoint, config.protoKey, creds);
279
+ }
280
+ const descriptor = loadedRoot.toDescriptor('proto3');
281
+ const definition = protoLoader.loadFileDescriptorSetFromObject(descriptor, reflectLoaderOptions);
282
+ const packageObject = grpc.loadPackageDefinition(definition);
283
+ const Service = lodash_1.default.get(packageObject, config.protoKey);
284
+ const endpointGrpcOptions = (0, common_2.isExtendedGrpcActionEndpoint)(endpointData)
285
+ ? endpointData.grpcOptions || {}
286
+ : {};
287
+ const serviceInstance = new Service(actionEndpoint, creds, Object.assign(Object.assign(Object.assign({}, constants_1.DEFAULT_GRPC_OPTIONS), grpcOptions), endpointGrpcOptions));
288
+ return serviceInstance;
289
+ }
290
+ function loadAndCachePackageObject(root, protoPath) {
291
+ var _a;
292
+ const cachedPackageObject = (_a = packageObjectsMap.get(root)) === null || _a === void 0 ? void 0 : _a[protoPath];
293
+ if (cachedPackageObject) {
294
+ return cachedPackageObject;
295
+ }
296
+ root.loadSync(protoPath);
297
+ const definition = protoLoader.loadSync(protoPath, grpcLoaderOptions);
298
+ const packageObject = grpc.loadPackageDefinition(definition);
299
+ let packageObjectsByRoot = packageObjectsMap.get(root);
300
+ if (!packageObjectsByRoot) {
301
+ packageObjectsByRoot = {};
302
+ packageObjectsMap.set(root, packageObjectsByRoot);
303
+ }
304
+ packageObjectsByRoot[protoPath] = packageObject;
305
+ return packageObject;
306
+ }
307
+ async function getServiceInstance(root, config, endpointData, grpcOptions, credentials) {
308
+ if ('reflection' in config) {
309
+ return getServiceInstanceReflectCached(config, endpointData, grpcOptions, credentials);
310
+ }
311
+ const actionEndpoint = createActionEndpoint(endpointData);
312
+ const cacheKey = [config.protoKey, actionEndpoint];
313
+ let serviceInstance = lodash_1.default.get(serviceInstancesMap, cacheKey);
314
+ if (!serviceInstance) {
315
+ const packageObject = loadAndCachePackageObject(root, config.protoPath);
316
+ const Service = lodash_1.default.get(packageObject, config.protoKey);
317
+ const creds = getChannelCredential(config, endpointData, credentials);
318
+ const endpointGrpcOptions = (0, common_2.isExtendedGrpcActionEndpoint)(endpointData)
319
+ ? endpointData.grpcOptions || {}
320
+ : {};
321
+ serviceInstance = new Service(actionEndpoint, creds, Object.assign(Object.assign(Object.assign({}, constants_1.DEFAULT_GRPC_OPTIONS), grpcOptions), endpointGrpcOptions));
322
+ // Save pointer to service in cache
323
+ lodash_1.default.set(serviceInstancesMap, cacheKey, serviceInstance);
324
+ }
325
+ return serviceInstance;
326
+ }
327
+ async function getResponseData({ config, response, ctx, packageRoot, args, ErrorConctructor, }) {
328
+ // Handle operation's runtime protocol buffers
329
+ if (response) {
330
+ const encodedFields = config.encodedFields;
331
+ if (Array.isArray(response)) {
332
+ response.forEach((responseItem) => decodeResponse(responseItem, packageRoot, ctx, encodedFields, ErrorConctructor));
333
+ }
334
+ else if (typeof response === 'object') {
335
+ decodeResponse(response, packageRoot, ctx, encodedFields, ErrorConctructor);
336
+ }
337
+ }
338
+ let responseData = response;
339
+ if (config.transformResponseData) {
340
+ try {
341
+ responseData = await config.transformResponseData(response, {
342
+ args,
343
+ ctx,
344
+ });
345
+ ctx.log('Transformed response data');
346
+ }
347
+ catch (error) {
348
+ (0, common_2.handleError)(ErrorConctructor, error, ctx, 'Transform response data failed');
349
+ }
350
+ }
351
+ return responseData;
352
+ }
353
+ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey, actionName, options, ErrorConctructor) {
354
+ const serviceName = (options === null || options === void 0 ? void 0 : options.serviceName) || serviceKey;
355
+ let getService;
356
+ let recreateService;
357
+ let getActionEndpoint;
358
+ const grpcOptions = (options === null || options === void 0 ? void 0 : options.grpcOptions) || {};
359
+ if (!('reflection' in config)) {
360
+ loadAndCachePackageObject(root, config.protoPath);
361
+ }
362
+ if (typeof config.endpoint === 'function') {
363
+ // ToDo: memoize services in cache keys depends on args
364
+ getActionEndpoint = (args) => {
365
+ const endpointData = config.endpoint(endpoints, args);
366
+ return createActionEndpoint(endpointData);
367
+ };
368
+ getService = (args) => {
369
+ const endpointData = config.endpoint(endpoints, args);
370
+ // Client will be created on first request because it depends on args
371
+ return getServiceInstance(root, config, endpointData, grpcOptions, credentials);
372
+ };
373
+ recreateService = (service, closeTimeout, ctx, args) => {
374
+ const actionEndpoint = getActionEndpoint(args);
375
+ const serviceInstancesCache = 'reflection' in config ? reflectionServiceInstancesMap : serviceInstancesMap;
376
+ const cachePath = [config.protoKey, actionEndpoint];
377
+ clearInstancesCache(service, serviceInstancesCache, cachePath, closeTimeout, ctx);
378
+ };
379
+ }
380
+ else if (endpoints) {
381
+ let endpointData = endpoints.grpcEndpoint || endpoints.endpoint;
382
+ if (config.endpoint) {
383
+ endpointData = endpoints[config.endpoint];
384
+ }
385
+ if (endpointData) {
386
+ const actionEndpoint = createActionEndpoint(endpointData);
387
+ if ('reflection' in config &&
388
+ (config.reflection === common_1.GrpcReflection.OnEveryRequest ||
389
+ config.reflection === common_1.GrpcReflection.OnFirstRequest)) {
390
+ getService = () => getServiceInstanceReflectCached(config, endpointData, grpcOptions, credentials);
391
+ recreateService = (service, closeTimeout, ctx) => {
392
+ const cachePath = [config.protoKey, actionEndpoint];
393
+ clearInstancesCache(service, reflectionServiceInstancesMap, cachePath, closeTimeout, ctx);
394
+ };
395
+ }
396
+ else {
397
+ getService = () => getServiceInstance(root, config, endpointData, grpcOptions, credentials);
398
+ recreateService = (service, closeTimeout, ctx) => {
399
+ const serviceInstancesCache = 'reflection' in config
400
+ ? reflectionServiceInstancesMap
401
+ : serviceInstancesMap;
402
+ const cachePath = [config.protoKey, actionEndpoint];
403
+ clearInstancesCache(service, serviceInstancesCache, cachePath, closeTimeout, ctx);
404
+ };
405
+ }
406
+ getActionEndpoint = () => actionEndpoint;
407
+ }
408
+ }
409
+ return async function action(actionConfig) {
410
+ const { args, requestId, headers, ctx: parentCtx } = actionConfig;
411
+ const { action } = config;
412
+ const lang = headers[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru; // header might be empty string
413
+ const ctx = parentCtx.create(`Gateway ${serviceName} ${actionName} [grpc]`, {
414
+ tags: {
415
+ action: actionName,
416
+ service: serviceName,
417
+ type: 'grpc',
418
+ },
419
+ });
420
+ const startRequestTime = Date.now();
421
+ const requestData = {
422
+ timestamp: startRequestTime,
423
+ service: serviceName,
424
+ action: actionName,
425
+ requestTime: 0,
426
+ requestId: actionConfig.requestId,
427
+ requestMethod: action,
428
+ requestUrl: config.protoKey,
429
+ };
430
+ const debugHeaders = {
431
+ 'x-api-request-action': action,
432
+ 'x-api-request-protokey': config.protoKey,
433
+ 'x-api-request-lang': lang,
434
+ 'x-request-id': requestId,
435
+ 'x-gateway-version': constants_1.VERSION,
436
+ };
437
+ if ('protoPath' in config) {
438
+ debugHeaders['x-api-request-protopath'] = config.protoPath;
439
+ }
440
+ ctx.log('Initiating request', { debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders) });
441
+ const sendStats = (status, data) => {
442
+ if (options === null || options === void 0 ? void 0 : options.sendStats) {
443
+ options.sendStats(Object.assign(Object.assign({}, data), { restStatus: status }), (0, redact_sensitive_headers_1.redactSensitiveHeaders)(parentCtx, headers), parentCtx, { debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders) });
444
+ }
445
+ else {
446
+ ctx.stats(Object.assign(Object.assign({}, requestData), { responseStatus: status }));
447
+ }
448
+ };
449
+ function processError(grpcError) {
450
+ var _a;
451
+ const restStatus = lodash_1.default.get(grpcError.getGatewayError(), 'status', 500);
452
+ sendStats(restStatus, Object.assign(Object.assign({}, requestData), { responseSize: (_a = grpcError.getRawError()) === null || _a === void 0 ? void 0 : _a.metadata, grpcStatus: grpcError.getGatewayError().code }));
453
+ ctx.logError('Request failed', ErrorConctructor.wrap(grpcError.getAppError(ErrorConctructor)), Object.assign({ serviceName,
454
+ actionName, debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders) }, grpcError.getGatewayError()));
455
+ ctx.end();
456
+ }
457
+ let params;
458
+ if (config.params) {
459
+ try {
460
+ params = await config.params(args, headers, { ctx });
461
+ }
462
+ catch (error) {
463
+ (0, common_2.handleError)(ErrorConctructor, error, ctx, 'Getting config params failed');
464
+ }
465
+ }
466
+ let service;
467
+ try {
468
+ service = await getService(args);
469
+ }
470
+ catch (error) {
471
+ (0, common_2.handleError)(ErrorConctructor, error, ctx, 'getService failed');
472
+ throw error;
473
+ }
474
+ // eslint-disable-next-line complexity
475
+ return new Promise((resolve, reject) => {
476
+ var _a, _b, _c, _d;
477
+ let endpointData = (endpoints === null || endpoints === void 0 ? void 0 : endpoints.grpcEndpoint) || (endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpoint);
478
+ if (typeof config.endpoint === 'function') {
479
+ endpointData = config.endpoint(endpoints, args);
480
+ }
481
+ else if (config.endpoint) {
482
+ endpointData = lodash_1.default.get(endpoints, [config.endpoint]);
483
+ }
484
+ if (!endpointData) {
485
+ const errorText = `Gateway config error. Endpoint has been not found in service "${serviceKey}"`;
486
+ throw new parse_error_1.GrpcError(errorText, {
487
+ status: 400,
488
+ code: 'ENDPOINT_NOT_FOUND',
489
+ message: errorText,
490
+ });
491
+ }
492
+ const actionEndpoint = getActionEndpoint(args);
493
+ debugHeaders['x-api-request-endpoint'] = actionEndpoint;
494
+ const validationSchema = config.validationSchema || options.validationSchema;
495
+ const invalidParams = validationSchema ? (0, validate_1.validateArgs)(args, validationSchema) : false;
496
+ if (invalidParams) {
497
+ throw new parse_error_1.GrpcError('Invalid params', {
498
+ status: 400,
499
+ code: 'INVALID_PARAMS',
500
+ message: 'Validation failed',
501
+ details: {
502
+ title: 'Invalid params',
503
+ description: invalidParams,
504
+ },
505
+ });
506
+ }
507
+ const timeout = (_c = (_b = (_a = actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.timeout) !== null && _a !== void 0 ? _a : config === null || config === void 0 ? void 0 : config.timeout) !== null && _b !== void 0 ? _b : options === null || options === void 0 ? void 0 : options.timeout) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_TIMEOUT;
508
+ const serviceOptions = {
509
+ deadline: Date.now() + timeout,
510
+ };
511
+ const { body = null } = params !== null && params !== void 0 ? params : { body: args };
512
+ const serviceMetadata = createMetadata({
513
+ options,
514
+ actionConfig,
515
+ config,
516
+ params,
517
+ serviceName,
518
+ ctx,
519
+ });
520
+ if (!service[action]) {
521
+ reject(new parse_error_1.GrpcError('Not found action', {
522
+ status: 400,
523
+ code: 'GRPC_ACTION_NOT_FOUND',
524
+ message: `Not found action ${action} in ${serviceKey}`,
525
+ }));
526
+ return;
527
+ }
528
+ switch (config.type) {
529
+ case 'serverStream': {
530
+ ctx.log('Creating serverStream request', {
531
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
532
+ });
533
+ const actionCall = service[action].bind(service);
534
+ const stream = actionCall(body, serviceMetadata, serviceOptions);
535
+ stream.on('error', (error) => {
536
+ ctx.log('ServerStream error', {
537
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
538
+ });
539
+ processError(new parse_error_1.GrpcError('ClientReadableStream error', (0, parse_error_1.parseGrpcError)(error, root, lang), error));
540
+ });
541
+ stream.on('status', (status) => {
542
+ ctx.log('ServerStream status changed', status);
543
+ });
544
+ stream.on('end', () => {
545
+ ctx.log('ServerStream request completed', {
546
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
547
+ });
548
+ ctx.end();
549
+ });
550
+ resolve({ debugHeaders, stream });
551
+ return;
552
+ }
553
+ case 'clientStream': {
554
+ ctx.log('Creating clientStream request', {
555
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
556
+ });
557
+ if (!actionConfig.callback) {
558
+ throw new parse_error_1.GrpcError('Invalid action type', {
559
+ status: 400,
560
+ code: 'ACTION_CALLBACK_REQUIRED',
561
+ message: `Client stream actions require callback function`,
562
+ });
563
+ }
564
+ const actionCall = service[action].bind(service);
565
+ const stream = actionCall(serviceMetadata, serviceOptions, actionConfig.callback);
566
+ resolve({ debugHeaders, stream });
567
+ return;
568
+ }
569
+ case 'bidi': {
570
+ ctx.log('Creating serverStream request', {
571
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
572
+ });
573
+ const actionCall = service[action].bind(service);
574
+ const stream = actionCall(serviceMetadata, serviceOptions);
575
+ stream.on('error', (error) => {
576
+ ctx.log('BidiStream error', {
577
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
578
+ });
579
+ processError(new parse_error_1.GrpcError('BidiStream error', (0, parse_error_1.parseGrpcError)(error, root, lang), error));
580
+ });
581
+ stream.on('status', (status) => {
582
+ ctx.log('BidiStream status changed', status);
583
+ });
584
+ stream.on('end', () => {
585
+ ctx.log('BidiStream request completed', {
586
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
587
+ });
588
+ ctx.end();
589
+ });
590
+ resolve({
591
+ debugHeaders,
592
+ stream,
593
+ });
594
+ return;
595
+ }
596
+ default: {
597
+ ctx.log('Starting unary request', {
598
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
599
+ });
600
+ let retries = (_d = config.retries) !== null && _d !== void 0 ? _d : 0;
601
+ let actionCall = service[action].bind(service);
602
+ const callAction = () => {
603
+ let trailingMetadata = {};
604
+ const call = actionCall(body, serviceMetadata, serviceOptions, async (error, response) => {
605
+ const endRequestTime = Date.now();
606
+ requestData.requestTime = endRequestTime - startRequestTime;
607
+ const shouldRecreateService = error &&
608
+ options.grpcRecreateService &&
609
+ (0, grpc_1.isRecreateServiceError)(error);
610
+ const shouldRetry = error && retries && (0, grpc_1.isRetryableError)(error);
611
+ if (shouldRecreateService) {
612
+ ctx.log(`Service client for ${config.protoKey} is going to be re-created`);
613
+ recreateService(service, timeout * 1.5, ctx, args);
614
+ }
615
+ if (shouldRetry) {
616
+ ctx.logError(`Request failed, retrying ${retries--} more times`);
617
+ // Update pointer to re-created client in local service variable
618
+ try {
619
+ service = await getService(args);
620
+ }
621
+ catch (error) {
622
+ (0, common_2.handleError)(ErrorConctructor, error, ctx, 'getService failed');
623
+ throw error;
624
+ }
625
+ // Update service
626
+ actionCall = service[action].bind(service);
627
+ callAction();
628
+ return;
629
+ }
630
+ if (error) {
631
+ reject(new parse_error_1.GrpcError('gRPC request error', (0, parse_error_1.parseGrpcError)(error, root, lang), error));
632
+ return;
633
+ }
634
+ const responseData = await getResponseData({
635
+ response,
636
+ ctx,
637
+ config,
638
+ args,
639
+ packageRoot: root,
640
+ ErrorConctructor,
641
+ });
642
+ Object.assign(debugHeaders, (0, common_2.getHeadersFromMetadata)(trailingMetadata));
643
+ sendStats(200, Object.assign(Object.assign({}, requestData), { responseSize: (0, object_sizeof_1.default)(response), grpcStatus: 0 }));
644
+ ctx.log('Request completed', {
645
+ debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders),
646
+ });
647
+ ctx.end();
648
+ return resolve({ responseData, debugHeaders });
649
+ });
650
+ call.on('status', (status) => {
651
+ trailingMetadata = status.metadata.toJSON();
652
+ });
653
+ };
654
+ callAction();
655
+ }
656
+ }
657
+ }).catch((error) => {
658
+ const grpcError = (0, parse_error_1.isGrpcError)(error) ? error : (0, parse_error_1.grpcErrorFactory)(error);
659
+ processError(grpcError);
660
+ return Promise.reject({ error: grpcError.getGatewayError(), debugHeaders });
661
+ });
662
+ };
663
+ }
664
+ exports.default = createGrpcAction;
@@ -0,0 +1,11 @@
1
+ import { ApiActionConfig, ApiByScope, ApiServiceMixedActionConfig, GatewayConfig, GatewayRequest, GatewayResponse, SchemasByScope } from '../models/common';
2
+ import { GatewayContext } from '../models/context';
3
+ import { AppErrorConstructor } from '../models/error';
4
+ import type { GrpcContext } from './grpc';
5
+ export declare function createMixedAction<TSchema extends SchemasByScope, Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse>(config: ApiServiceMixedActionConfig<Context, Req, Res, any, any, any>, api: ApiByScope<TSchema, Context, Req, Res>, serviceName: string, actionName: string, extra: {
6
+ config: GatewayConfig<Context, Req, Res>;
7
+ grpcContext: GrpcContext;
8
+ }, ErrorConctructor: AppErrorConstructor): (actionConfig: ApiActionConfig<Context, any>) => Promise<{
9
+ responseData: any;
10
+ debugHeaders: {};
11
+ }>;