@gravity-ui/gateway 4.7.0 → 4.7.1-alpha.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 (95) hide show
  1. package/README.md +128 -3
  2. package/{build → dist/commonjs}/components/grpc.d.ts +5 -5
  3. package/{build → dist/commonjs}/components/grpc.js +94 -84
  4. package/{build → dist/commonjs}/components/mixed.d.ts +4 -4
  5. package/{build → dist/commonjs}/components/mixed.js +11 -12
  6. package/{build → dist/commonjs}/components/rest.d.ts +5 -5
  7. package/{build → dist/commonjs}/components/rest.js +33 -33
  8. package/{build → dist/commonjs}/constants.d.ts +2 -2
  9. package/{build → dist/commonjs}/constants.js +29 -19
  10. package/{build → dist/commonjs}/index.d.ts +9 -8
  11. package/{build → dist/commonjs}/index.js +35 -46
  12. package/{build → dist/commonjs}/models/common.d.ts +12 -7
  13. package/{build → dist/commonjs}/models/context.d.ts +0 -1
  14. package/dist/commonjs/package.json +3 -0
  15. package/dist/commonjs/utils/axios.d.ts +4 -0
  16. package/{build → dist/commonjs}/utils/axios.js +3 -4
  17. package/{build → dist/commonjs}/utils/common.d.ts +4 -4
  18. package/{build → dist/commonjs}/utils/common.js +8 -8
  19. package/{build → dist/commonjs}/utils/create-context-api.d.ts +2 -2
  20. package/{build → dist/commonjs}/utils/create-context-api.js +5 -6
  21. package/{build → dist/commonjs}/utils/grpc-reflection.js +15 -35
  22. package/{build → dist/commonjs}/utils/grpc.d.ts +1 -1
  23. package/{build → dist/commonjs}/utils/grpc.js +10 -11
  24. package/dist/commonjs/utils/overrideEndpoints/index.d.ts +2 -0
  25. package/dist/commonjs/utils/overrideEndpoints/index.js +4 -0
  26. package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.d.ts +1 -1
  27. package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.js +1 -2
  28. package/dist/commonjs/utils/package-root.d.ts +1 -0
  29. package/dist/commonjs/utils/package-root.js +44 -0
  30. package/{build → dist/commonjs}/utils/parse-error.d.ts +5 -5
  31. package/{build → dist/commonjs}/utils/parse-error.js +19 -19
  32. package/{build → dist/commonjs}/utils/proto-path-resolver.d.ts +1 -1
  33. package/{build → dist/commonjs}/utils/proto-path-resolver.js +24 -15
  34. package/{build → dist/commonjs}/utils/redact-sensitive-headers.d.ts +1 -2
  35. package/{build → dist/commonjs}/utils/redact-sensitive-headers.js +1 -2
  36. package/dist/commonjs/utils/source-dir.d.ts +1 -0
  37. package/dist/commonjs/utils/source-dir.js +41 -0
  38. package/{build → dist/commonjs}/utils/typed-api.d.ts +1 -1
  39. package/{build → dist/commonjs}/utils/typed-api.js +1 -2
  40. package/{build → dist/commonjs}/utils/validate.js +6 -10
  41. package/dist/esm/components/grpc.d.ts +24 -0
  42. package/dist/esm/components/grpc.js +691 -0
  43. package/dist/esm/components/mixed.d.ts +11 -0
  44. package/dist/esm/components/mixed.js +62 -0
  45. package/dist/esm/components/rest.d.ts +8 -0
  46. package/dist/esm/components/rest.js +357 -0
  47. package/dist/esm/constants.d.ts +53 -0
  48. package/dist/esm/constants.js +82 -0
  49. package/dist/esm/index.d.ts +13 -0
  50. package/dist/esm/index.js +274 -0
  51. package/dist/esm/models/common.d.ts +289 -0
  52. package/dist/esm/models/common.js +5 -0
  53. package/dist/esm/models/context.d.ts +22 -0
  54. package/dist/esm/models/context.js +1 -0
  55. package/dist/esm/models/error.d.ts +12 -0
  56. package/dist/esm/models/error.js +1 -0
  57. package/dist/esm/package.json +3 -0
  58. package/{build → dist/esm}/utils/axios.d.ts +1 -1
  59. package/dist/esm/utils/axios.js +24 -0
  60. package/dist/esm/utils/common.d.ts +16 -0
  61. package/dist/esm/utils/common.js +48 -0
  62. package/dist/esm/utils/create-context-api.d.ts +4 -0
  63. package/dist/esm/utils/create-context-api.js +38 -0
  64. package/dist/esm/utils/grpc-reflection.d.ts +28 -0
  65. package/dist/esm/utils/grpc-reflection.js +72 -0
  66. package/dist/esm/utils/grpc.d.ts +15 -0
  67. package/dist/esm/utils/grpc.js +72 -0
  68. package/dist/esm/utils/overrideEndpoints/index.d.ts +2 -0
  69. package/dist/esm/utils/overrideEndpoints/index.js +2 -0
  70. package/dist/esm/utils/overrideEndpoints/overrideEndpoints.d.ts +17 -0
  71. package/dist/esm/utils/overrideEndpoints/overrideEndpoints.js +96 -0
  72. package/dist/esm/utils/package-root.d.ts +1 -0
  73. package/dist/esm/utils/package-root.js +8 -0
  74. package/dist/esm/utils/parse-error.d.ts +30 -0
  75. package/dist/esm/utils/parse-error.js +214 -0
  76. package/dist/esm/utils/proto-path-resolver.d.ts +2 -0
  77. package/dist/esm/utils/proto-path-resolver.js +23 -0
  78. package/dist/esm/utils/redact-sensitive-headers.d.ts +3 -0
  79. package/dist/esm/utils/redact-sensitive-headers.js +12 -0
  80. package/dist/esm/utils/source-dir.d.ts +1 -0
  81. package/dist/esm/utils/source-dir.js +4 -0
  82. package/dist/esm/utils/typed-api.d.ts +2 -0
  83. package/dist/esm/utils/typed-api.js +3 -0
  84. package/dist/esm/utils/validate.d.ts +4 -0
  85. package/dist/esm/utils/validate.js +47 -0
  86. package/package.json +41 -16
  87. package/build/utils/overrideEndpoints/index.d.ts +0 -2
  88. package/build/utils/overrideEndpoints/index.js +0 -4
  89. /package/bin/{patch.js → patch.cjs} +0 -0
  90. /package/{build → dist/commonjs}/models/common.js +0 -0
  91. /package/{build → dist/commonjs}/models/context.js +0 -0
  92. /package/{build → dist/commonjs}/models/error.d.ts +0 -0
  93. /package/{build → dist/commonjs}/models/error.js +0 -0
  94. /package/{build → dist/commonjs}/utils/grpc-reflection.d.ts +0 -0
  95. /package/{build → dist/commonjs}/utils/validate.d.ts +0 -0
@@ -0,0 +1,274 @@
1
+ import _ from 'lodash';
2
+ import { createGrpcAction, createRoot, getCredentialsMap } from './components/grpc.js';
3
+ import { createMixedAction } from './components/mixed.js';
4
+ import { createRestAction } from './components/rest.js';
5
+ import { ANY_ACTION_SYMBOL } from './constants.js';
6
+ import { getKeys, handleError } from './utils/common.js';
7
+ import { overrideEndpoints } from './utils/overrideEndpoints/overrideEndpoints.js';
8
+ export * from './utils/typed-api.js';
9
+ export * from './utils/grpc-reflection.js';
10
+ export { isRetryableGrpcError } from './utils/grpc.js';
11
+ export * from './models/common.js';
12
+ export * from './models/context.js';
13
+ export * from './models/error.js';
14
+ function isMixedActionConfig(actionConfig) {
15
+ return typeof actionConfig === 'function';
16
+ }
17
+ function isRestActionConfig(actionConfig) {
18
+ return Boolean(actionConfig.method);
19
+ }
20
+ function createApiAction(schema, config, serviceKey, actionName, api, grpcContext) {
21
+ var _a;
22
+ const serviceSchema = schema[serviceKey];
23
+ if (!serviceSchema) {
24
+ throw new config.ErrorConstructor(`Gateway config error. Service "${serviceKey}" have been not found.`, {
25
+ code: 'SERVICE_NOT_FOUND',
26
+ });
27
+ }
28
+ const action = serviceSchema.actions[actionName];
29
+ if (!action) {
30
+ throw new config.ErrorConstructor(`Gateway config error. Action "${serviceKey}.${actionName}" have been not found.`, {
31
+ code: 'ACTION_NOT_FOUND',
32
+ });
33
+ }
34
+ const serviceName = serviceSchema.serviceName || '';
35
+ const installation = config.installation || '';
36
+ const env = config.env || '';
37
+ if (isMixedActionConfig(action)) {
38
+ const resultServiceName = serviceName || serviceKey;
39
+ return createMixedAction(action, api, resultServiceName, actionName, { config, grpcContext }, config.ErrorConstructor);
40
+ }
41
+ const endpointsConfig = _.get(serviceSchema.endpoints, [installation, env]);
42
+ if (isRestActionConfig(action)) {
43
+ return createRestAction(endpointsConfig, action, serviceKey, actionName, {
44
+ serviceName,
45
+ timeout: config.timeout,
46
+ sendStats: config.sendStats,
47
+ proxyHeaders: config.proxyHeaders,
48
+ proxyDebugHeaders: config.proxyDebugHeaders,
49
+ axiosConfig: config.axiosConfig,
50
+ axiosInterceptors: config.axiosInterceptors,
51
+ axiosRetryCondition: config.axiosRetryCondition,
52
+ validationSchema: config.validationSchema,
53
+ encodePathArgs: config.encodePathArgs,
54
+ getAuthHeaders: config.getAuthHeaders,
55
+ }, config.ErrorConstructor);
56
+ }
57
+ const grpcRecreateService = (_a = config.grpcRecreateService) !== null && _a !== void 0 ? _a : true;
58
+ return createGrpcAction(grpcContext, endpointsConfig, action, serviceKey, actionName, {
59
+ serviceName,
60
+ timeout: config.timeout,
61
+ sendStats: config.sendStats,
62
+ proxyHeaders: config.proxyHeaders,
63
+ proxyDebugHeaders: config.proxyDebugHeaders,
64
+ grpcRetryCondition: config.grpcRetryCondition,
65
+ grpcOptions: config.grpcOptions,
66
+ grpcRecreateService,
67
+ getAuthHeaders: config.getAuthHeaders,
68
+ }, config.ErrorConstructor);
69
+ }
70
+ function generateGatewayApi(schema, config, grpcContext, baseApi) {
71
+ const { installation, env } = config;
72
+ if (!installation) {
73
+ throw new config.ErrorConstructor('Gateway config error', {
74
+ code: 'EMPTY_GATEWAY_INSTALLATION',
75
+ });
76
+ }
77
+ if (!env) {
78
+ throw new config.ErrorConstructor('Gateway config error', { code: 'EMPTY_GATEWAY_ENV' });
79
+ }
80
+ return Object.keys(schema).reduce((api, serviceKey) => {
81
+ return _.set(api, serviceKey, Object.keys(schema[serviceKey].actions).reduce((serviceActions, actionName) => (Object.assign(Object.assign({}, serviceActions), { [actionName]: createApiAction(schema, config, serviceKey, actionName, baseApi, grpcContext) })), {}));
82
+ }, {});
83
+ }
84
+ function generateGatewayApiController(schemasByScope, Api, config, controllerActions) {
85
+ // eslint-disable-next-line complexity
86
+ return async function gateway(req, res) {
87
+ var _a, _b, _c;
88
+ const { userId } = res.locals || {};
89
+ const { service, action, scope = 'root' } = req.params;
90
+ const withDebugHeaders = typeof config.withDebugHeaders === 'function'
91
+ ? config.withDebugHeaders(req, res)
92
+ : Boolean(config.withDebugHeaders);
93
+ const onUnknownAction = config === null || config === void 0 ? void 0 : config.onUnknownAction;
94
+ const onBeforeAction = config === null || config === void 0 ? void 0 : config.onBeforeAction;
95
+ const onRequestSuccess = config === null || config === void 0 ? void 0 : config.onRequestSuccess;
96
+ const onRequestFailed = config === null || config === void 0 ? void 0 : config.onRequestFailed;
97
+ if (!Api || !Api[scope] || !Api[scope][service]) {
98
+ if (onUnknownAction) {
99
+ return onUnknownAction(req, res, {
100
+ service,
101
+ });
102
+ }
103
+ else {
104
+ return res.status(404).send({
105
+ status: 404,
106
+ code: 'UNKNOWN_SERVICE',
107
+ message: 'Unknown service',
108
+ });
109
+ }
110
+ }
111
+ if (!Api[scope][service][action]) {
112
+ if (onUnknownAction) {
113
+ return onUnknownAction(req, res, {
114
+ service,
115
+ action,
116
+ });
117
+ }
118
+ else {
119
+ return res.status(404).send({
120
+ status: 404,
121
+ code: 'UNKNOWN_SERVICE_ACTION',
122
+ message: 'Unknown service action',
123
+ });
124
+ }
125
+ }
126
+ // Validation of private endpoints, preventing their invocation through the controller.
127
+ if (_.startsWith(action, '_')) {
128
+ return res.status(404).send({
129
+ status: 404,
130
+ code: 'UNKNOWN_SERVICE_ACTION',
131
+ message: 'Unknown service action',
132
+ });
133
+ }
134
+ if (controllerActions &&
135
+ _.get(controllerActions, [scope]) !== ANY_ACTION_SYMBOL &&
136
+ _.get(controllerActions, [scope, service]) !== ANY_ACTION_SYMBOL &&
137
+ !_.get(controllerActions, [scope, service, action])) {
138
+ return res.status(404).send({
139
+ status: 404,
140
+ code: 'UNKNOWN_SERVICE_ACTION',
141
+ message: 'Unknown service action',
142
+ });
143
+ }
144
+ const args = req.method === 'GET' ? req.query : req.body;
145
+ try {
146
+ const abortController = new AbortController();
147
+ const handleCloseConnection = () => {
148
+ abortController.abort();
149
+ };
150
+ req.connection.once('close', handleCloseConnection);
151
+ const apiAction = Api[scope][service][action];
152
+ if (onBeforeAction) {
153
+ const actionConfig = (_c = (_b = (_a = schemasByScope[scope]) === null || _a === void 0 ? void 0 : _a[service]) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c[action];
154
+ try {
155
+ await onBeforeAction(req, res, scope, service, action, actionConfig);
156
+ }
157
+ catch (error) {
158
+ handleError(config.ErrorConstructor, error, req.ctx, 'Before action handler error');
159
+ throw { error, debugHeaders: {} };
160
+ }
161
+ }
162
+ const { responseData, responseHeaders, debugHeaders } = await apiAction({
163
+ requestId: req.id,
164
+ headers: req.headers,
165
+ ctx: req.ctx,
166
+ args,
167
+ authArgs: config.getAuthArgs(req, res),
168
+ userId,
169
+ abortSignal: abortController.signal,
170
+ });
171
+ req.connection.removeListener('close', handleCloseConnection);
172
+ if (withDebugHeaders) {
173
+ res.set(debugHeaders);
174
+ }
175
+ if (responseHeaders) {
176
+ res.set(responseHeaders);
177
+ }
178
+ if (onRequestSuccess) {
179
+ return onRequestSuccess(req, res, responseData);
180
+ }
181
+ else {
182
+ return res.send(responseData);
183
+ }
184
+ }
185
+ catch (respError) {
186
+ const { error, debugHeaders } = respError;
187
+ let responseError = error;
188
+ if (withDebugHeaders) {
189
+ res.set(debugHeaders);
190
+ }
191
+ else {
192
+ responseError = _.omit(error, ['debug']);
193
+ // Remove DebugInfo
194
+ if (responseError.details) {
195
+ _.forEach(responseError.details, function (value, key) {
196
+ const DEBUG_INFO_TYPE = 'type.googleapis.com/google.rpc.DebugInfo';
197
+ if ((value === null || value === void 0 ? void 0 : value['@type']) === DEBUG_INFO_TYPE) {
198
+ responseError.details[key] = undefined;
199
+ }
200
+ });
201
+ }
202
+ }
203
+ if (onRequestFailed) {
204
+ return onRequestFailed(req, res, error);
205
+ }
206
+ else {
207
+ return res.status(_.get(error, 'status', 500)).send(responseError);
208
+ }
209
+ }
210
+ };
211
+ }
212
+ export function getGatewayControllers(schemasByScope, config) {
213
+ var _a;
214
+ const apiByScope = {};
215
+ config.installation = config.installation || process.env.APP_INSTALLATION;
216
+ config.env = config.env || process.env.APP_ENV;
217
+ if (process.env.GATEWAY_ENDPOINTS_OVERRIDES) {
218
+ try {
219
+ // eslint-disable-next-line no-param-reassign
220
+ schemasByScope = overrideEndpoints(schemasByScope, JSON.parse(process.env.GATEWAY_ENDPOINTS_OVERRIDES), config.installation, config.env);
221
+ }
222
+ catch (err) {
223
+ console.warn('Error when parse GATEWAY_ENDPOINTS_OVERRIDES', err);
224
+ }
225
+ }
226
+ const credentials = getCredentialsMap(config.caCertificatePath);
227
+ for (const scope of getKeys(schemasByScope)) {
228
+ apiByScope[scope] = generateGatewayApi(schemasByScope[scope], config, { root: createRoot(config.includeProtoRoots), credentials }, apiByScope);
229
+ }
230
+ const api = Object.assign({}, apiByScope);
231
+ const rootScope = apiByScope.root;
232
+ if (rootScope) {
233
+ for (const rootService of getKeys(rootScope)) {
234
+ const curScope = (_a = api[rootService]) !== null && _a !== void 0 ? _a : {};
235
+ for (const rootAction of getKeys(rootScope[rootService])) {
236
+ const rootServiceFunc = rootScope[rootService][rootAction];
237
+ if (curScope[rootAction]) {
238
+ for (const curScopeAction of getKeys(curScope[rootAction])) {
239
+ rootServiceFunc[curScopeAction] = curScope[rootAction][curScopeAction];
240
+ }
241
+ }
242
+ curScope[rootAction] = rootServiceFunc;
243
+ }
244
+ api[rootService] = curScope;
245
+ }
246
+ }
247
+ const controllerActions = config.actions
248
+ ? config.actions.reduce((acc, item) => {
249
+ const [scope, service, action] = item.split('.');
250
+ // Ignore option '*' for scopes
251
+ if (scope === ANY_ACTION_SYMBOL) {
252
+ return acc;
253
+ }
254
+ if (service === ANY_ACTION_SYMBOL) {
255
+ _.set(acc, [scope], ANY_ACTION_SYMBOL);
256
+ }
257
+ else if (action === ANY_ACTION_SYMBOL) {
258
+ if (acc[scope] !== ANY_ACTION_SYMBOL) {
259
+ _.set(acc, [scope, service], ANY_ACTION_SYMBOL);
260
+ }
261
+ }
262
+ else {
263
+ _.set(acc, [scope, service, action], true);
264
+ }
265
+ return acc;
266
+ }, {})
267
+ : undefined;
268
+ const controller = generateGatewayApiController(schemasByScope, api, config, controllerActions);
269
+ return {
270
+ controller,
271
+ api,
272
+ };
273
+ }
274
+ export default getGatewayControllers;
@@ -0,0 +1,289 @@
1
+ import { IncomingHttpHeaders } from 'http';
2
+ import { ClientDuplexStream, ClientReadableStream, ClientWritableStream, type ServiceError } from '@grpc/grpc-js';
3
+ import type { HandlerType } from '@grpc/grpc-js/build/src/server-call.js';
4
+ import { AxiosInterceptorManager, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
5
+ import { IAxiosRetryConfig } from 'axios-retry';
6
+ import type { Request, Response } from 'express';
7
+ import protobufjs from 'protobufjs';
8
+ import type { GrpcContext } from '../components/grpc.js';
9
+ import { Lang } from '../constants.js';
10
+ import { GatewayContext } from './context.js';
11
+ import { AppErrorConstructor } from './error.js';
12
+ export interface GatewayRequest<Context extends GatewayContext> extends Request {
13
+ id: string;
14
+ ctx: Context;
15
+ }
16
+ export interface GatewayResponse extends Response {
17
+ }
18
+ export interface Headers {
19
+ [key: string]: any;
20
+ }
21
+ export interface EndpointsConfig {
22
+ [key: string]: ActionEndpoint;
23
+ }
24
+ export type AuthHeadersFunc = (serviceName: string) => Record<string, string> | undefined;
25
+ export interface ApiActionConfig<Context extends GatewayContext, TRequestData, TResponseData = any> {
26
+ requestId: string;
27
+ headers: Headers;
28
+ args: TRequestData;
29
+ ctx: Context;
30
+ timeout?: number;
31
+ callback?: (response: TResponseData) => void;
32
+ authArgs?: Record<string, unknown>;
33
+ userId?: string;
34
+ abortSignal?: AbortSignal;
35
+ }
36
+ export interface GRPCActionData {
37
+ [key: string]: unknown;
38
+ }
39
+ export type SendStats<Context extends GatewayContext> = (stats: Stats, headers: IncomingHttpHeaders, ctx: Context, meta: {
40
+ debugHeaders: Headers;
41
+ }) => void;
42
+ export interface Stats {
43
+ service: string;
44
+ action: string;
45
+ restStatus: number;
46
+ grpcStatus?: number;
47
+ responseSize: number;
48
+ requestId: string;
49
+ requestTime: number;
50
+ requestMethod: string;
51
+ requestUrl: string;
52
+ timestamp: number;
53
+ userId?: string;
54
+ traceId: string;
55
+ }
56
+ export type ControllerType = 'rest' | 'grpc';
57
+ export interface GatewayError {
58
+ status: number;
59
+ message: string;
60
+ code: string;
61
+ details?: {
62
+ title?: string;
63
+ description?: string;
64
+ } & Record<string, unknown>;
65
+ debug?: any;
66
+ requestId?: string;
67
+ }
68
+ export type GrpcRetryCondition = (error: ServiceError) => boolean;
69
+ export type AxiosRetryCondition = IAxiosRetryConfig['retryCondition'];
70
+ export type ProxyHeadersFunctionExtra = {
71
+ service: string;
72
+ action: string;
73
+ protopath?: string;
74
+ protokey?: string;
75
+ };
76
+ export type ProxyHeadersFunction = (headers: IncomingHttpHeaders, type: ControllerType, extra: ProxyHeadersFunctionExtra) => IncomingHttpHeaders;
77
+ export type ProxyHeaders = string[] | ProxyHeadersFunction;
78
+ export type ProxyResponseHeadersFunction = (headers: Headers, type: ControllerType) => Headers;
79
+ export type ProxyResponseHeaders = string[] | ProxyResponseHeadersFunction;
80
+ export type GetAuthHeadersParams<AuthArgs = Record<string, unknown>> = {
81
+ actionType: ControllerType;
82
+ serviceName: string;
83
+ requestHeaders: Headers;
84
+ authArgs: AuthArgs | undefined;
85
+ };
86
+ export type GetAuthHeaders<AuthArgs = Record<string, unknown>> = (params: GetAuthHeadersParams<AuthArgs>) => Record<string, string> | undefined;
87
+ export type ResponseContentType = AxiosResponse['headers']['Content-Type'];
88
+ export interface GatewayApiOptions<Context extends GatewayContext> {
89
+ serviceName: string;
90
+ timeout?: number;
91
+ grpcRetryCondition?: GrpcRetryCondition;
92
+ axiosRetryCondition?: AxiosRetryCondition;
93
+ sendStats?: SendStats<Context>;
94
+ grpcOptions?: object;
95
+ grpcRecreateService?: boolean;
96
+ axiosConfig?: AxiosRequestConfig;
97
+ axiosInterceptors?: AxiosInterceptorsConfig;
98
+ proxyHeaders?: ProxyHeaders;
99
+ proxyDebugHeaders?: ProxyHeaders;
100
+ validationSchema?: object;
101
+ encodePathArgs?: boolean;
102
+ expectedResponseContentType?: ResponseContentType | ResponseContentType[];
103
+ getAuthHeaders: GetAuthHeaders;
104
+ }
105
+ export interface ParamsOutput {
106
+ body?: any;
107
+ query?: {
108
+ [key: string]: any;
109
+ };
110
+ headers?: Headers;
111
+ }
112
+ export interface ResponseError extends AxiosResponse<unknown> {
113
+ }
114
+ export interface ExtendedBaseActionEndpoint {
115
+ path: string;
116
+ }
117
+ export interface ExtendedGrpcActionEndpoint extends ExtendedBaseActionEndpoint {
118
+ insecure?: boolean;
119
+ secureWithoutRootCert?: boolean;
120
+ grpcOptions?: object;
121
+ }
122
+ export interface ExtendedRestActionEndpoint extends ExtendedBaseActionEndpoint {
123
+ axiosConfig?: AxiosRequestConfig;
124
+ }
125
+ export type ExtendedActionEndpoint = ExtendedGrpcActionEndpoint | ExtendedRestActionEndpoint;
126
+ export type ActionEndpoint = string | ExtendedActionEndpoint;
127
+ export interface ApiServiceBaseActionConfig<Context extends GatewayContext, TOutput, TParams = undefined, TTransformed = TOutput> {
128
+ getAuthHeaders?: GetAuthHeaders;
129
+ params?: (args: TParams, headers: Headers, config: ApiParamsConfig<Context>) => Promise<ParamsOutput> | ParamsOutput;
130
+ endpoint?: string | ((endpoints: EndpointsConfig | undefined, args: TParams) => string);
131
+ transformResponseData?: (data: TOutput, config: ApiTransformResponseDataConfig<TParams, Context>) => Promise<TTransformed> | TTransformed;
132
+ transformResponseError?: (error: ResponseError, config: ApiTransformResponseErrorConfig<TParams, Context>) => Promise<GatewayError> | GatewayError;
133
+ validationSchema?: object;
134
+ timeout?: number;
135
+ retries?: number;
136
+ idempotency?: boolean;
137
+ proxyHeaders?: ProxyHeaders;
138
+ proxyResponseHeaders?: ProxyResponseHeaders;
139
+ metadata?: Record<string, string | number | boolean>;
140
+ abortOnClientDisconnect?: boolean;
141
+ }
142
+ export interface ApiServiceRestActionConfig<Context extends GatewayContext, TOutput, TParams = undefined, TTransformed = TOutput> extends ApiServiceBaseActionConfig<Context, TOutput, TParams, TTransformed> {
143
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
144
+ path: (args: TParams) => string;
145
+ paramsSerializer?: AxiosRequestConfig['paramsSerializer'];
146
+ responseType?: AxiosRequestConfig['responseType'];
147
+ expectedResponseContentType?: ResponseContentType | ResponseContentType[];
148
+ maxRedirects?: number;
149
+ axiosRetryCondition?: AxiosRetryCondition;
150
+ }
151
+ export interface ApiServiceBaseGrpcActionConfig<Context extends GatewayContext, TOutput, TParams = undefined, TTransformed = TOutput> extends ApiServiceBaseActionConfig<Context, TOutput, TParams, TTransformed> {
152
+ action: string;
153
+ protoKey: string;
154
+ insecure?: boolean;
155
+ secureWithoutRootCert?: boolean;
156
+ encodedFields?: string[];
157
+ type?: HandlerType;
158
+ decodeAnyMessageProtoLoaderOptions?: protobufjs.IConversionOptions;
159
+ }
160
+ export interface ApiServiceFileGrpcActionConfig<Context extends GatewayContext, TOutput, TParams, TTransformed> extends ApiServiceBaseGrpcActionConfig<Context, TOutput, TParams, TTransformed> {
161
+ protoPath: string;
162
+ }
163
+ export declare enum GrpcReflection {
164
+ OnFirstRequest = 0,
165
+ OnEveryRequest = 1
166
+ }
167
+ export interface ApiServiceReflectGrpcActionConfig<Context extends GatewayContext, TOutput, TParams, TTransformed> extends ApiServiceBaseGrpcActionConfig<Context, TOutput, TParams, TTransformed> {
168
+ reflection: GrpcReflection;
169
+ reflectionRefreshSec?: number;
170
+ }
171
+ export type ApiServiceGrpcActionConfig<Context extends GatewayContext, TOutput, TParams = undefined, TTransformed = TOutput> = ApiServiceFileGrpcActionConfig<Context, TOutput, TParams, TTransformed> | ApiServiceReflectGrpcActionConfig<Context, TOutput, TParams, TTransformed>;
172
+ export interface ApiServiceMixedExtra<Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse> {
173
+ headers: Headers;
174
+ lang: Lang;
175
+ ctx: Context;
176
+ config: GatewayConfig<Context, Req, Res>;
177
+ grpcContext: GrpcContext;
178
+ abortSignal?: AbortSignal;
179
+ }
180
+ export type ApiServiceMixedActionConfig<Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse, TOutput, TParams = undefined, TTransformed = TOutput> = (api: unknown, args: TParams, extra: ApiServiceMixedExtra<Context, Req, Res>) => Promise<TTransformed>;
181
+ export type ApiServiceActionConfig<Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse, TOutput, TParams = undefined, TTransformed = TOutput> = ApiServiceRestActionConfig<Context, TOutput, TParams, TTransformed> | ApiServiceGrpcActionConfig<Context, TOutput, TParams, TTransformed> | ApiServiceMixedActionConfig<Context, Req, Res, TOutput, TParams, TTransformed>;
182
+ export type ApiActionResponseType<T> = T extends ApiServiceActionConfig<any, any, any, any, any, infer R> ? R : never;
183
+ export type ApiActionParams<T> = T extends ApiServiceActionConfig<any, any, any, any, infer R, any> ? R : never;
184
+ export interface ApiParamsConfig<Context extends GatewayContext> {
185
+ ctx: Context;
186
+ }
187
+ export interface ApiTransformResponseDataConfig<TParams, Context extends GatewayContext> {
188
+ args: TParams;
189
+ ctx: Context;
190
+ headers?: Headers;
191
+ }
192
+ export interface ApiTransformResponseErrorConfig<TParams, Context extends GatewayContext> {
193
+ args: TParams;
194
+ ctx: Context;
195
+ }
196
+ export interface BaseSchema {
197
+ [key: string]: {
198
+ actions: Record<string, ApiServiceActionConfig<any, any, any, any, any, any>>;
199
+ serviceName?: string;
200
+ endpoints?: Record<string, Record<string, EndpointsConfig>>;
201
+ };
202
+ }
203
+ export interface SchemasByScope {
204
+ [scope: string]: BaseSchema;
205
+ }
206
+ export type ApiByScope<R extends SchemasByScope, Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse> = {
207
+ [scope in keyof R]: GatewayApi<R[scope], Context, Req, Res>;
208
+ };
209
+ export type ApiWithRoot<R extends SchemasByScope, Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse> = ApiByScope<R, Context, Req, Res>['root'] & ApiByScope<R, Context, Req, Res>;
210
+ export interface GatewayActionHeaders {
211
+ debugHeaders: Headers;
212
+ }
213
+ export interface GatewayActionUnaryResponse<TAction> extends GatewayActionHeaders {
214
+ responseData: ApiActionResponseType<TAction>;
215
+ responseHeaders?: Headers;
216
+ }
217
+ export interface GatewayActionClientStreamResponse<TAction> extends GatewayActionHeaders {
218
+ stream: ClientWritableStream<ApiActionResponseType<TAction>>;
219
+ responseData?: never;
220
+ responseHeaders?: never;
221
+ }
222
+ export interface GatewayActionServerStreamResponse<TAction> extends GatewayActionHeaders {
223
+ stream: ClientReadableStream<ApiActionResponseType<TAction>>;
224
+ responseData?: never;
225
+ responseHeaders?: never;
226
+ }
227
+ export interface GatewayActionDuplexStreamResponse<TAction> extends GatewayActionHeaders {
228
+ stream: ClientDuplexStream<ApiActionParams<TAction>, ApiActionResponseType<TAction>>;
229
+ responseData?: never;
230
+ responseHeaders?: never;
231
+ }
232
+ export type GatewayActionResponseData<Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse, TAction extends ApiServiceActionConfig<Context, Req, Res, unknown, unknown, unknown>> = TAction extends ApiServiceGrpcActionConfig<Context, unknown, unknown, unknown> ? TAction['type'] extends 'clientStream' ? GatewayActionClientStreamResponse<TAction> : TAction['type'] extends 'serverStream' ? GatewayActionServerStreamResponse<TAction> : TAction['type'] extends 'bidi' ? GatewayActionDuplexStreamResponse<TAction> : GatewayActionUnaryResponse<TAction> : GatewayActionUnaryResponse<TAction>;
233
+ type GatewayAction<Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse, TAction extends ApiServiceActionConfig<Context, Req, Res, unknown, unknown, unknown>> = undefined extends ApiActionParams<TAction> ? (params?: ApiActionConfig<Context, ApiActionParams<TAction>, ApiActionResponseType<TAction>>) => Promise<GatewayActionResponseData<Context, Req, Res, TAction>> : (params: ApiActionConfig<Context, ApiActionParams<TAction>, ApiActionResponseType<TAction>>) => Promise<GatewayActionResponseData<Context, Req, Res, TAction>>;
234
+ export type GatewayApi<T extends BaseSchema, Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse> = {
235
+ [S in keyof T]: {
236
+ [A in keyof T[S]['actions']]: GatewayAction<Context, Req, Res, T[S]['actions'][A]>;
237
+ };
238
+ };
239
+ type ContextAction<TAction> = undefined extends ApiActionParams<TAction> ? (params?: ApiActionParams<TAction>) => Promise<ApiActionResponseType<TAction>> : (params: ApiActionParams<TAction>) => Promise<ApiActionResponseType<TAction>>;
240
+ export type ContextApi<T extends BaseSchema> = {
241
+ [S in keyof T]: {
242
+ [A in keyof T[S]['actions']]: ContextAction<T[S]['actions'][A]>;
243
+ };
244
+ };
245
+ export type ContextApiByScope<R extends SchemasByScope> = {
246
+ [scope in keyof R]: ContextApi<R[scope]>;
247
+ };
248
+ export type ContextApiWithRoot<R extends SchemasByScope> = ContextApiByScope<R>['root'] & ContextApiByScope<R>;
249
+ interface OnUnknownActionData {
250
+ service?: string;
251
+ action?: string;
252
+ }
253
+ type AxiosInterceptorUseParams<T> = Parameters<AxiosInterceptorManager<T>['use']>;
254
+ interface AxiosInterceptorConfig<T> {
255
+ callback: AxiosInterceptorUseParams<T>[0];
256
+ errorCallback?: AxiosInterceptorUseParams<T>[1];
257
+ }
258
+ export interface AxiosInterceptorsConfig {
259
+ request?: AxiosInterceptorConfig<InternalAxiosRequestConfig<any>>[];
260
+ response?: AxiosInterceptorConfig<AxiosResponse<any>>[];
261
+ }
262
+ export interface GatewayConfig<Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse> {
263
+ installation?: string;
264
+ env?: string;
265
+ actions?: string[];
266
+ timeout?: number;
267
+ grpcRetryCondition?: GrpcRetryCondition;
268
+ axiosRetryCondition?: AxiosRetryCondition;
269
+ grpcOptions?: object;
270
+ grpcRecreateService?: boolean;
271
+ axiosConfig?: AxiosRequestConfig;
272
+ axiosInterceptors?: AxiosInterceptorsConfig;
273
+ onUnknownAction?: (req: Req, res: Res, data: OnUnknownActionData) => any;
274
+ onBeforeAction?: (req: Req, res: Res, scope: string, service: string, action: string, config?: ApiServiceActionConfig<Context, Req, Res, unknown>) => any;
275
+ onRequestSuccess?: (req: Req, res: Res, data: any) => any;
276
+ onRequestFailed?: (req: Req, res: Res, error: any) => any;
277
+ sendStats?: SendStats<Context>;
278
+ includeProtoRoots?: string[];
279
+ caCertificatePath: string | null;
280
+ proxyHeaders: ProxyHeaders;
281
+ proxyDebugHeaders?: ProxyHeaders;
282
+ withDebugHeaders?: boolean | ((req: Req, res: Res) => boolean);
283
+ validationSchema?: object;
284
+ encodePathArgs?: boolean;
285
+ getAuthArgs: (req: Req, res: Res) => Record<string, unknown> | undefined;
286
+ getAuthHeaders: GetAuthHeaders;
287
+ ErrorConstructor: AppErrorConstructor;
288
+ }
289
+ export {};
@@ -0,0 +1,5 @@
1
+ export var GrpcReflection;
2
+ (function (GrpcReflection) {
3
+ GrpcReflection[GrpcReflection["OnFirstRequest"] = 0] = "OnFirstRequest";
4
+ GrpcReflection[GrpcReflection["OnEveryRequest"] = 1] = "OnEveryRequest";
5
+ })(GrpcReflection || (GrpcReflection = {}));
@@ -0,0 +1,22 @@
1
+ import { IncomingHttpHeaders } from 'http';
2
+ export interface GatewayContextParams {
3
+ tags?: Record<string, string | number | boolean | undefined>;
4
+ }
5
+ export type Dict = {
6
+ [key: string]: unknown;
7
+ };
8
+ export interface GatewayContext {
9
+ create: (name: string, params?: GatewayContextParams) => this;
10
+ log: (message: string, extra?: Dict) => void;
11
+ logError: (message: string, error?: unknown, extra?: Dict) => void;
12
+ end: () => void;
13
+ stats: (data: {
14
+ [name: string]: string | number;
15
+ }) => void;
16
+ utils?: {
17
+ redactSensitiveHeaders?: (headers?: IncomingHttpHeaders) => IncomingHttpHeaders;
18
+ redactSensitiveKeys?: (headers: Dict) => Dict;
19
+ };
20
+ getMetadata: () => IncomingHttpHeaders;
21
+ getTraceId?: () => string | undefined;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export interface AppErrorArgs {
2
+ code?: string | number;
3
+ details?: object;
4
+ debug?: object;
5
+ }
6
+ export interface AppErrorWrapArgs extends AppErrorArgs {
7
+ message?: string;
8
+ }
9
+ export interface AppErrorConstructor {
10
+ new (message?: string, args?: AppErrorArgs): Error;
11
+ wrap: (error: Error, args?: AppErrorWrapArgs) => Error;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -1,4 +1,4 @@
1
1
  import { AxiosRequestConfig } from 'axios';
2
2
  import { IAxiosRetryConfig } from 'axios-retry';
3
- import { AxiosInterceptorsConfig } from '../models/common';
3
+ import { AxiosInterceptorsConfig } from '../models/common.js';
4
4
  export declare function getAxiosClient(timeout?: number, retries?: number, customRetryCondition?: IAxiosRetryConfig['retryCondition'], axiosConfig?: AxiosRequestConfig, { request: reqInterceptors, response: resInterceptors }?: AxiosInterceptorsConfig): import("axios").AxiosInstance;
@@ -0,0 +1,24 @@
1
+ import axios from 'axios';
2
+ import axiosRetry from 'axios-retry';
3
+ import _ from 'lodash';
4
+ import { DEFAULT_AXIOS_OPTIONS, DEFAULT_TIMEOUT } from '../constants.js';
5
+ export function getAxiosClient(timeout = DEFAULT_TIMEOUT, retries = 0, customRetryCondition, axiosConfig = DEFAULT_AXIOS_OPTIONS, { request: reqInterceptors, response: resInterceptors } = {}) {
6
+ const client = axios.create(Object.assign(Object.assign({}, axiosConfig), { timeout }));
7
+ reqInterceptors === null || reqInterceptors === void 0 ? void 0 : reqInterceptors.forEach(({ callback, errorCallback }) => client.interceptors.request.use(callback, errorCallback));
8
+ resInterceptors === null || resInterceptors === void 0 ? void 0 : resInterceptors.forEach(({ callback, errorCallback }) => client.interceptors.response.use(callback, errorCallback));
9
+ axiosRetry(client, {
10
+ retries,
11
+ retryDelay: axiosRetry.exponentialDelay,
12
+ retryCondition: (error) => {
13
+ var _a;
14
+ if (!error.config) {
15
+ return false;
16
+ }
17
+ return ((_a = customRetryCondition === null || customRetryCondition === void 0 ? void 0 : customRetryCondition(error)) !== null && _a !== void 0 ? _a : (axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error)));
18
+ },
19
+ onRetry: (retryCount, _error, requestConfig) => {
20
+ _.set(requestConfig, ['headers', 'x-request-attempt'], retryCount);
21
+ },
22
+ });
23
+ return client;
24
+ }
@@ -0,0 +1,16 @@
1
+ import * as grpc from '@grpc/grpc-js';
2
+ import _ from 'lodash';
3
+ import { ActionEndpoint, ApiServiceGrpcActionConfig, ExtendedActionEndpoint, ExtendedGrpcActionEndpoint, ExtendedRestActionEndpoint, Headers, ProxyHeadersFunctionExtra } from '../models/common.js';
4
+ import { Dict, GatewayContext } from '../models/context.js';
5
+ import { AppErrorConstructor } from '../models/error.js';
6
+ export declare function isExtendedActionEndpoint(endpoint: ActionEndpoint): endpoint is ExtendedActionEndpoint;
7
+ export declare function isExtendedGrpcActionEndpoint(endpoint: ActionEndpoint): endpoint is ExtendedGrpcActionEndpoint;
8
+ export declare function isExtendedRestActionEndpoint(endpoint: ActionEndpoint): endpoint is ExtendedRestActionEndpoint;
9
+ export declare function getKeys<T extends object>(obj: T): (keyof T)[];
10
+ /**
11
+ * This function should only use to sanitize debugHeaders that are creating in our code
12
+ */
13
+ export declare function sanitizeDebugHeaders(debugHeaders: Headers): _.Omit<Headers, "x-api-request-body">;
14
+ export declare function getHeadersFromMetadata(metadata: Record<string, grpc.MetadataValue[]>, prefix?: string): Record<string, string>;
15
+ export declare function handleError<Context extends GatewayContext>(ErrorConstructor: AppErrorConstructor, error: unknown, ctx: Context, message: string, extra?: Dict): void;
16
+ export declare const getProxyHeadersArgs: <Context extends GatewayContext>(serviceName: string, actionName: string, grpcConfig?: ApiServiceGrpcActionConfig<Context, any, any>) => ProxyHeadersFunctionExtra;