@gravity-ui/gateway 2.5.4 → 2.6.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.
@@ -45,7 +45,13 @@ const parse_error_1 = require("../utils/parse-error");
45
45
  const proto_path_resolver_1 = require("../utils/proto-path-resolver");
46
46
  const redact_sensitive_headers_1 = require("../utils/redact-sensitive-headers");
47
47
  const validate_1 = require("../utils/validate");
48
- const grpcLoaderOptions = Object.assign(Object.assign({}, constants_1.DEFAULT_PROTO_LOADER_OPTIONS), { includeDirs: [path_1.default.join(__dirname, '../../proto')] });
48
+ const reflectLoaderOptions = {
49
+ longs: String,
50
+ enums: String,
51
+ defaults: true,
52
+ oneofs: true,
53
+ };
54
+ const grpcLoaderOptions = Object.assign(Object.assign({}, reflectLoaderOptions), { includeDirs: [path_1.default.join(__dirname, '../../proto')] });
49
55
  function createRoot(includeGrpcPaths) {
50
56
  const root = new protobufjs.Root();
51
57
  root.loadSync(path_1.default.resolve(__dirname, '../../proto/google/rpc/code.proto'));
@@ -75,10 +81,16 @@ function decodeResponse(response, packageRoot, ctx, encodedFields = [], ErrorCon
75
81
  const systemFields = ['metadata', 'response', 'error.details'];
76
82
  [...systemFields, ...encodedFields].forEach((fieldName) => {
77
83
  try {
78
- const parsedFieldName = fieldName.replace(/\.\*$/, '');
84
+ const traverseRegExp = /\.\*$/;
85
+ const needTraverse = traverseRegExp.test(fieldName);
86
+ const parsedFieldName = needTraverse
87
+ ? fieldName.replace(traverseRegExp, '')
88
+ : fieldName;
79
89
  const fieldValue = lodash_1.default.get(response, parsedFieldName);
80
90
  if (fieldValue) {
81
- lodash_1.default.set(response, parsedFieldName, (0, grpc_1.decodeAnyMessageRecursively)(packageRoot, fieldValue));
91
+ lodash_1.default.set(response, parsedFieldName, needTraverse
92
+ ? (0, grpc_1.traverseAnyMessage)(packageRoot, fieldValue)
93
+ : (0, grpc_1.decodeAnyMessageRecursively)(packageRoot, fieldValue));
82
94
  }
83
95
  }
84
96
  catch (error) {
@@ -258,7 +270,7 @@ async function getServiceInstanceReflect(config, endpointData, grpcOptions, cred
258
270
  loadedRoot = await (0, grpc_reflection_1.getCachedReflectionRoot)(actionEndpoint, config.protoKey, creds, combinedGrpcOptions);
259
271
  }
260
272
  const descriptor = loadedRoot.toDescriptor('proto3');
261
- const definition = protoLoader.loadFileDescriptorSetFromObject(descriptor, constants_1.DEFAULT_PROTO_LOADER_OPTIONS);
273
+ const definition = protoLoader.loadFileDescriptorSetFromObject(descriptor, reflectLoaderOptions);
262
274
  const packageObject = grpc.loadPackageDefinition(definition);
263
275
  const Service = lodash_1.default.get(packageObject, config.protoKey);
264
276
  const serviceInstance = new Service(actionEndpoint, creds, combinedGrpcOptions);
@@ -384,7 +396,8 @@ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey,
384
396
  }
385
397
  }
386
398
  return async function action(actionConfig) {
387
- const { args, requestId, headers, ctx: parentCtx } = actionConfig;
399
+ var _a;
400
+ const { args, requestId, headers, ctx: parentCtx, userId } = actionConfig;
388
401
  const { action } = config;
389
402
  const lang = headers[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru; // header might be empty string
390
403
  const ctx = parentCtx.create(`Gateway ${serviceName} ${actionName} [grpc]`, {
@@ -403,6 +416,8 @@ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey,
403
416
  requestId: actionConfig.requestId,
404
417
  requestMethod: action,
405
418
  requestUrl: config.protoKey,
419
+ traceId: ((_a = ctx.getTraceId) === null || _a === void 0 ? void 0 : _a.call(ctx)) || '',
420
+ userId: userId || '',
406
421
  };
407
422
  const debugHeaders = {
408
423
  'x-api-request-action': action,
@@ -39,8 +39,8 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
39
39
  const defaultAxiosClient = (0, axios_1.getAxiosClient)(timeout, config === null || config === void 0 ? void 0 : config.retries, options === null || options === void 0 ? void 0 : options.axiosConfig);
40
40
  /* eslint-disable complexity */
41
41
  return async function action(actionConfig) {
42
- var _a, _b, _c, _d, _e, _f, _g;
43
- const { args, requestId, headers: requestHeaders, ctx: parentCtx, authArgs } = actionConfig;
42
+ var _a, _b, _c, _d, _e, _f, _g, _h;
43
+ const { args, requestId, headers: requestHeaders, ctx: parentCtx, authArgs, userId, } = actionConfig;
44
44
  const debugHeaders = {};
45
45
  const lang = requestHeaders[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru; // header might be empty string
46
46
  const serviceName = (options === null || options === void 0 ? void 0 : options.serviceName) || serviceKey;
@@ -210,6 +210,8 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
210
210
  requestId,
211
211
  requestMethod: config.method,
212
212
  requestUrl: actionURL,
213
+ traceId: ((_f = ctx.getTraceId) === null || _f === void 0 ? void 0 : _f.call(ctx)) || '',
214
+ userId: userId || '',
213
215
  };
214
216
  const requestConfig = {
215
217
  url: actionURL,
@@ -245,7 +247,7 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
245
247
  const responseHeaders = {};
246
248
  const endRequestTime = Date.now();
247
249
  requestData.requestTime = endRequestTime - startRequestTime;
248
- const actualResponseContentType = (_f = response.headers) === null || _f === void 0 ? void 0 : _f['Content-Type'];
250
+ const actualResponseContentType = (_g = response.headers) === null || _g === void 0 ? void 0 : _g['Content-Type'];
249
251
  const expectedResponseContentType = config.expectedResponseContentType || options.expectedResponseContentType;
250
252
  if (actualResponseContentType && expectedResponseContentType) {
251
253
  let isInvalidResponseContentType;
@@ -342,7 +344,7 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
342
344
  }
343
345
  const responseStatus = lodash_1.default.get(parsedError, 'status') || lodash_1.default.get(error, 'status', 500);
344
346
  if (options === null || options === void 0 ? void 0 : options.sendStats) {
345
- options.sendStats(Object.assign(Object.assign({}, requestData), { responseSize: getRestResponseSize((_g = error === null || error === void 0 ? void 0 : error.response) === null || _g === void 0 ? void 0 : _g.data, ctx, ErrorConstructor), restStatus: responseStatus }), (0, redact_sensitive_headers_1.redactSensitiveHeaders)(parentCtx, headers), parentCtx, { debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders) });
347
+ options.sendStats(Object.assign(Object.assign({}, requestData), { responseSize: getRestResponseSize((_h = error === null || error === void 0 ? void 0 : error.response) === null || _h === void 0 ? void 0 : _h.data, ctx, ErrorConstructor), restStatus: responseStatus, userId }), (0, redact_sensitive_headers_1.redactSensitiveHeaders)(parentCtx, headers), parentCtx, { debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders) });
346
348
  }
347
349
  else {
348
350
  ctx.stats(Object.assign(Object.assign({}, requestData), { responseStatus }));
@@ -20,12 +20,6 @@ export declare const DEFAULT_GRPC_OPTIONS: {
20
20
  'grpc.keepalive_timeout_ms': number;
21
21
  'grpc.keepalive_permit_without_calls': number;
22
22
  };
23
- export declare const DEFAULT_PROTO_LOADER_OPTIONS: {
24
- longs: StringConstructor;
25
- enums: StringConstructor;
26
- defaults: boolean;
27
- oneofs: boolean;
28
- };
29
23
  /**
30
24
  * Byte sizes are taken from ECMAScript Language Specification
31
25
  * http://www.ecma-international.org/ecma-262/5.1/
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.AXIOS_RETRY_NAMESPACE = exports.DEFAULT_VALIDATION_SCHEMA = exports.RECREATE_SERVICE_CODES = exports.RETRYABLE_STATUS_CODES = exports.ANY_ACTION_SYMBOL = exports.ECMA_STRING_SIZE = exports.DEFAULT_PROTO_LOADER_OPTIONS = exports.DEFAULT_GRPC_OPTIONS = exports.DEFAULT_AXIOS_OPTIONS = exports.DEFAULT_PROXY_HEADERS = exports.DEFAULT_LANG_HEADER = exports.DEFAULT_TIMEOUT = exports.Lang = exports.VERSION = void 0;
29
+ exports.AXIOS_RETRY_NAMESPACE = exports.DEFAULT_VALIDATION_SCHEMA = exports.RECREATE_SERVICE_CODES = exports.RETRYABLE_STATUS_CODES = exports.ANY_ACTION_SYMBOL = exports.ECMA_STRING_SIZE = exports.DEFAULT_GRPC_OPTIONS = exports.DEFAULT_AXIOS_OPTIONS = exports.DEFAULT_PROXY_HEADERS = exports.DEFAULT_LANG_HEADER = exports.DEFAULT_TIMEOUT = exports.Lang = exports.VERSION = void 0;
30
30
  const http_1 = __importDefault(require("http"));
31
31
  const https_1 = __importDefault(require("https"));
32
32
  const grpc = __importStar(require("@grpc/grpc-js"));
@@ -63,12 +63,6 @@ exports.DEFAULT_GRPC_OPTIONS = {
63
63
  'grpc.keepalive_timeout_ms': 1000,
64
64
  'grpc.keepalive_permit_without_calls': 1,
65
65
  };
66
- exports.DEFAULT_PROTO_LOADER_OPTIONS = {
67
- longs: String,
68
- enums: String,
69
- defaults: true,
70
- oneofs: true,
71
- };
72
66
  /**
73
67
  * Byte sizes are taken from ECMAScript Language Specification
74
68
  * http://www.ecma-international.org/ecma-262/5.1/
package/build/index.js CHANGED
@@ -112,6 +112,7 @@ function generateGatewayApiController(schemasByScope, Api, config, controllerAct
112
112
  // eslint-disable-next-line complexity
113
113
  return async function gateway(req, res) {
114
114
  var _a, _b, _c;
115
+ const { userId } = res.locals || {};
115
116
  const { service, action, scope = 'root' } = req.params;
116
117
  const withDebugHeaders = typeof config.withDebugHeaders === 'function'
117
118
  ? config.withDebugHeaders(req, res)
@@ -186,6 +187,7 @@ function generateGatewayApiController(schemasByScope, Api, config, controllerAct
186
187
  ctx: req.ctx,
187
188
  args,
188
189
  authArgs: config.getAuthArgs(req, res),
190
+ userId,
189
191
  });
190
192
  if (withDebugHeaders) {
191
193
  res.set(debugHeaders);
@@ -29,6 +29,7 @@ export interface ApiActionConfig<Context extends GatewayContext, TRequestData, T
29
29
  timeout?: number;
30
30
  callback?: (response: TResponseData) => void;
31
31
  authArgs?: Record<string, unknown>;
32
+ userId?: string;
32
33
  }
33
34
  export interface GRPCActionData {
34
35
  [key: string]: unknown;
@@ -47,6 +48,8 @@ export interface Stats {
47
48
  requestMethod: string;
48
49
  requestUrl: string;
49
50
  timestamp: number;
51
+ userId?: string;
52
+ traceId: string;
50
53
  }
51
54
  export type ControllerType = 'rest' | 'grpc';
52
55
  export interface GatewayError {
@@ -19,4 +19,5 @@ export interface GatewayContext {
19
19
  redactSensitiveKeys?: (headers: Dict) => Dict;
20
20
  };
21
21
  getMetadata: () => IncomingHttpHeaders;
22
+ getTraceId?: () => string;
22
23
  }
@@ -1,5 +1,15 @@
1
+ /// <reference types="node" />
1
2
  import * as grpc from '@grpc/grpc-js';
2
3
  import * as protobufjs from 'protobufjs';
3
- export declare function decodeAnyMessageRecursively(root: protobufjs.Root, message?: unknown): unknown;
4
+ export declare function decodeAnyMessageRecursively(root: protobufjs.Root, message?: {
5
+ type_url?: string;
6
+ value?: Buffer;
7
+ }): {
8
+ type_url?: string | undefined;
9
+ value?: Buffer | undefined;
10
+ } | {
11
+ [k: string]: any;
12
+ } | undefined;
13
+ export declare function traverseAnyMessage(root: protobufjs.Root, message?: any): any;
4
14
  export declare function isRetryableError(error?: grpc.ServiceError): boolean;
5
15
  export declare function isRecreateServiceError(error?: grpc.ServiceError): boolean;
@@ -1,34 +1,44 @@
1
1
  "use strict";
2
2
  /* eslint-disable camelcase */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.isRecreateServiceError = exports.isRetryableError = exports.decodeAnyMessageRecursively = void 0;
4
+ exports.isRecreateServiceError = exports.isRetryableError = exports.traverseAnyMessage = exports.decodeAnyMessageRecursively = void 0;
5
5
  const constants_1 = require("../constants");
6
- function isEncodedMessage(message) {
7
- return Boolean(message.type_url && message.value);
8
- }
9
6
  function decodeAnyMessageRecursively(root, message) {
10
- if (!message || typeof message !== 'object') {
7
+ if (!message || !message.type_url || !message.value) {
11
8
  return message;
12
9
  }
13
- if (Array.isArray(message)) {
14
- return message.map((innerMessage) => decodeAnyMessageRecursively(root, innerMessage));
15
- }
16
- if (typeof message === 'object' && !isEncodedMessage(message)) {
17
- return Object.entries(message).reduce((res, [key, value]) => {
18
- res[key] = decodeAnyMessageRecursively(root, value);
19
- return res;
20
- }, {});
21
- }
22
10
  const lastSlashIndex = message.type_url.lastIndexOf('/');
23
11
  if (lastSlashIndex < 0) {
24
12
  return message;
25
13
  }
26
14
  const typeName = message.type_url.substring(lastSlashIndex + 1);
27
15
  const type = root.lookupType(typeName);
28
- const decodedMessage = type.toObject(type.decode(message.value), constants_1.DEFAULT_PROTO_LOADER_OPTIONS);
29
- return decodeAnyMessageRecursively(root, decodedMessage);
16
+ const data = type.decode(message.value).toJSON();
17
+ Object.keys(data).forEach((key) => {
18
+ data[key] = decodeAnyMessageRecursively(root, data[key]);
19
+ });
20
+ return data;
30
21
  }
31
22
  exports.decodeAnyMessageRecursively = decodeAnyMessageRecursively;
23
+ function traverseAnyMessage(root, message) {
24
+ if (!message) {
25
+ return message;
26
+ }
27
+ if (Array.isArray(message)) {
28
+ return message.map((item) => {
29
+ return traverseAnyMessage(root, item);
30
+ });
31
+ }
32
+ if (typeof message === 'object') {
33
+ Object.keys(message).forEach((key) => {
34
+ if (message[key]) {
35
+ message[key] = traverseAnyMessage(root, message[key]);
36
+ }
37
+ });
38
+ }
39
+ return decodeAnyMessageRecursively(root, message);
40
+ }
41
+ exports.traverseAnyMessage = traverseAnyMessage;
32
42
  function isRetryableError(error) {
33
43
  if (!error) {
34
44
  return false;
@@ -182,7 +182,7 @@ function parseGrpcError(error, packageRoot, lang = constants_1.Lang.Ru) {
182
182
  message: String(description || DEFAULT_GATEWAY_MESSAGE),
183
183
  code: DEFAULT_GATEWAY_CODE,
184
184
  details: Object.assign({ title,
185
- description, grpcCode: code }, (typeof details === 'object' && !Array.isArray(details) ? details : { details })),
185
+ description, grpcCode: code }, details),
186
186
  };
187
187
  }
188
188
  exports.parseGrpcError = parseGrpcError;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/gateway",
3
- "version": "2.5.4",
3
+ "version": "2.6.1",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",
@@ -43,7 +43,7 @@
43
43
  "axios": "^1.3.5",
44
44
  "axios-retry": "^3.4.0",
45
45
  "lodash": "^4.17.21",
46
- "object-sizeof": "^2.6.1",
46
+ "object-sizeof": "^2.6.5",
47
47
  "protobufjs": "^7.2.5",
48
48
  "uuid": "^9.0.0"
49
49
  },