@gravity-ui/gateway 4.1.0 → 4.1.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.
- package/{build → dist/commonjs}/components/grpc.d.ts +4 -4
- package/{build → dist/commonjs}/components/grpc.js +85 -75
- package/{build → dist/commonjs}/components/mixed.d.ts +4 -4
- package/{build → dist/commonjs}/components/mixed.js +11 -12
- package/{build → dist/commonjs}/components/rest.d.ts +5 -5
- package/{build → dist/commonjs}/components/rest.js +32 -32
- package/{build → dist/commonjs}/constants.d.ts +2 -2
- package/{build → dist/commonjs}/constants.js +25 -18
- package/{build → dist/commonjs}/index.d.ts +8 -7
- package/{build → dist/commonjs}/index.js +32 -44
- package/{build → dist/commonjs}/models/common.d.ts +11 -6
- package/{build → dist/commonjs}/models/context.d.ts +0 -1
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/utils/axios.d.ts +3 -0
- package/{build → dist/commonjs}/utils/axios.js +3 -4
- package/{build → dist/commonjs}/utils/common.d.ts +3 -3
- package/{build → dist/commonjs}/utils/common.js +7 -8
- package/{build → dist/commonjs}/utils/create-context-api.d.ts +2 -2
- package/{build → dist/commonjs}/utils/create-context-api.js +5 -6
- package/{build → dist/commonjs}/utils/grpc-reflection.js +21 -12
- package/{build → dist/commonjs}/utils/grpc.js +7 -8
- package/dist/commonjs/utils/overrideEndpoints/index.d.ts +2 -0
- package/dist/commonjs/utils/overrideEndpoints/index.js +4 -0
- package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.d.ts +1 -1
- package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.js +1 -2
- package/{build → dist/commonjs}/utils/parse-error.d.ts +4 -4
- package/{build → dist/commonjs}/utils/parse-error.js +18 -18
- package/{build → dist/commonjs}/utils/proto-path-resolver.js +23 -17
- package/{build → dist/commonjs}/utils/redact-sensitive-headers.d.ts +1 -2
- package/{build → dist/commonjs}/utils/redact-sensitive-headers.js +1 -2
- package/dist/commonjs/utils/source-dir.d.ts +1 -0
- package/dist/commonjs/utils/source-dir.js +41 -0
- package/{build → dist/commonjs}/utils/typed-api.d.ts +1 -1
- package/{build → dist/commonjs}/utils/typed-api.js +1 -2
- package/{build → dist/commonjs}/utils/validate.js +6 -10
- package/dist/esm/components/grpc.d.ts +24 -0
- package/dist/esm/components/grpc.js +641 -0
- package/dist/esm/components/mixed.d.ts +11 -0
- package/dist/esm/components/mixed.js +62 -0
- package/dist/esm/components/rest.d.ts +8 -0
- package/dist/esm/components/rest.js +349 -0
- package/dist/esm/constants.d.ts +53 -0
- package/dist/esm/constants.js +79 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +264 -0
- package/dist/esm/models/common.d.ts +272 -0
- package/dist/esm/models/common.js +5 -0
- package/dist/esm/models/context.d.ts +22 -0
- package/dist/esm/models/context.js +1 -0
- package/dist/esm/models/error.d.ts +12 -0
- package/dist/esm/models/error.js +1 -0
- package/dist/esm/package.json +3 -0
- package/{build → dist/esm}/utils/axios.d.ts +1 -1
- package/dist/esm/utils/axios.js +23 -0
- package/dist/esm/utils/common.d.ts +15 -0
- package/dist/esm/utils/common.js +38 -0
- package/dist/esm/utils/create-context-api.d.ts +4 -0
- package/dist/esm/utils/create-context-api.js +38 -0
- package/dist/esm/utils/grpc-reflection.d.ts +28 -0
- package/dist/esm/utils/grpc-reflection.js +68 -0
- package/dist/esm/utils/grpc.d.ts +5 -0
- package/dist/esm/utils/grpc.js +39 -0
- package/dist/esm/utils/overrideEndpoints/index.d.ts +2 -0
- package/dist/esm/utils/overrideEndpoints/index.js +2 -0
- package/dist/esm/utils/overrideEndpoints/overrideEndpoints.d.ts +17 -0
- package/dist/esm/utils/overrideEndpoints/overrideEndpoints.js +96 -0
- package/dist/esm/utils/parse-error.d.ts +30 -0
- package/dist/esm/utils/parse-error.js +210 -0
- package/dist/esm/utils/proto-path-resolver.d.ts +2 -0
- package/dist/esm/utils/proto-path-resolver.js +23 -0
- package/dist/esm/utils/redact-sensitive-headers.d.ts +3 -0
- package/dist/esm/utils/redact-sensitive-headers.js +12 -0
- package/dist/esm/utils/source-dir.d.ts +1 -0
- package/dist/esm/utils/source-dir.js +4 -0
- package/dist/esm/utils/typed-api.d.ts +2 -0
- package/dist/esm/utils/typed-api.js +3 -0
- package/dist/esm/utils/validate.d.ts +4 -0
- package/dist/esm/utils/validate.js +47 -0
- package/package.json +41 -16
- package/build/utils/overrideEndpoints/index.d.ts +0 -2
- package/build/utils/overrideEndpoints/index.js +0 -4
- /package/bin/{patch.js → patch.cjs} +0 -0
- /package/{build → dist/commonjs}/models/common.js +0 -0
- /package/{build → dist/commonjs}/models/context.js +0 -0
- /package/{build → dist/commonjs}/models/error.d.ts +0 -0
- /package/{build → dist/commonjs}/models/error.js +0 -0
- /package/{build → dist/commonjs}/utils/grpc-reflection.d.ts +0 -0
- /package/{build → dist/commonjs}/utils/grpc.d.ts +0 -0
- /package/{build → dist/commonjs}/utils/proto-path-resolver.d.ts +0 -0
- /package/{build → dist/commonjs}/utils/validate.d.ts +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { DEFAULT_LANG_HEADER, Lang } from '../constants.js';
|
|
13
|
+
import { handleError } from '../utils/common.js';
|
|
14
|
+
import { generateContextApi } from '../utils/create-context-api.js';
|
|
15
|
+
import { parseMixedError } from '../utils/parse-error.js';
|
|
16
|
+
export function createMixedAction(config, api, serviceName, actionName, extra, ErrorConstructor) {
|
|
17
|
+
return async (actionConfig) => {
|
|
18
|
+
const { args } = actionConfig, context = __rest(actionConfig, ["args"]);
|
|
19
|
+
const ctx = actionConfig.ctx.create(`Gateway ${serviceName} ${actionName} [mixed]`, {
|
|
20
|
+
tags: {
|
|
21
|
+
action: actionName,
|
|
22
|
+
service: serviceName,
|
|
23
|
+
type: 'mixed',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const contextApi = generateContextApi(api, Object.assign(Object.assign({}, context), { ctx }));
|
|
27
|
+
try {
|
|
28
|
+
const responseData = await config(contextApi, args, Object.assign({ headers: actionConfig.headers, lang: actionConfig.headers[DEFAULT_LANG_HEADER] || Lang.Ru, ctx }, extra));
|
|
29
|
+
ctx.log('Request completed');
|
|
30
|
+
return {
|
|
31
|
+
responseData,
|
|
32
|
+
debugHeaders: {},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
if (e instanceof Object && 'error' in e) {
|
|
37
|
+
handleError(ErrorConstructor, e, ctx, 'Request failed', {
|
|
38
|
+
actionName,
|
|
39
|
+
serviceName,
|
|
40
|
+
});
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
if (e instanceof Error) {
|
|
44
|
+
const parsedError = parseMixedError(e);
|
|
45
|
+
handleError(ErrorConstructor, e, ctx, 'Request failed', {
|
|
46
|
+
actionName,
|
|
47
|
+
serviceName,
|
|
48
|
+
});
|
|
49
|
+
throw {
|
|
50
|
+
error: parsedError,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
handleError(ErrorConstructor, e, ctx, 'Request failed');
|
|
54
|
+
throw {
|
|
55
|
+
error: e,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
ctx.end();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ApiActionConfig, ApiServiceRestActionConfig, EndpointsConfig, GatewayApiOptions, Headers } from '../models/common.js';
|
|
2
|
+
import { GatewayContext } from '../models/context.js';
|
|
3
|
+
import { AppErrorConstructor } from '../models/error.js';
|
|
4
|
+
export declare function createRestAction<Context extends GatewayContext>(endpoints: EndpointsConfig | undefined, config: ApiServiceRestActionConfig<Context, any, any>, serviceKey: string, actionName: string, options: GatewayApiOptions<Context>, ErrorConstructor: AppErrorConstructor): (actionConfig: ApiActionConfig<Context, any>) => Promise<{
|
|
5
|
+
responseData: unknown;
|
|
6
|
+
responseHeaders?: Headers;
|
|
7
|
+
debugHeaders: Headers;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import querystring from 'querystring';
|
|
2
|
+
import url from 'url';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { DEFAULT_LANG_HEADER, DEFAULT_PROXY_HEADERS, DEFAULT_TIMEOUT, ECMA_STRING_SIZE, Lang, VERSION, } from '../constants.js';
|
|
6
|
+
import { getAxiosClient } from '../utils/axios.js';
|
|
7
|
+
import { handleError, isExtendedActionEndpoint, isExtendedRestActionEndpoint, sanitizeDebugHeaders, } from '../utils/common.js';
|
|
8
|
+
import { parseRestError } from '../utils/parse-error.js';
|
|
9
|
+
import { redactSensitiveHeaders } from '../utils/redact-sensitive-headers.js';
|
|
10
|
+
import { encodePathParams, getPathArgsProxy, validateArgs } from '../utils/validate.js';
|
|
11
|
+
function getRestResponseSize(data, ctx, ErrorConstructor) {
|
|
12
|
+
var _a;
|
|
13
|
+
let responseSize = 0;
|
|
14
|
+
try {
|
|
15
|
+
responseSize = ECMA_STRING_SIZE * ((_a = JSON.stringify(data)) === null || _a === void 0 ? void 0 : _a.length);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
handleError(ErrorConstructor, error, ctx, 'Calculate response size failed');
|
|
19
|
+
}
|
|
20
|
+
return responseSize;
|
|
21
|
+
}
|
|
22
|
+
function getConfigSerializerFunction(config) {
|
|
23
|
+
if (typeof config.paramsSerializer === 'function') {
|
|
24
|
+
return config.paramsSerializer;
|
|
25
|
+
}
|
|
26
|
+
if (config.paramsSerializer && 'serialize' in config.paramsSerializer) {
|
|
27
|
+
return config.paramsSerializer.serialize;
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
export function createRestAction(endpoints, config, serviceKey, actionName, options, ErrorConstructor) {
|
|
32
|
+
var _a, _b, _c;
|
|
33
|
+
const timeout = (_c = (_a = config === null || config === void 0 ? void 0 : config.timeout) !== null && _a !== void 0 ? _a : (_b = options === null || options === void 0 ? void 0 : options.axiosConfig) === null || _b === void 0 ? void 0 : _b.timeout) !== null && _c !== void 0 ? _c : options === null || options === void 0 ? void 0 : options.timeout;
|
|
34
|
+
const defaultAxiosClient = getAxiosClient(timeout, config === null || config === void 0 ? void 0 : config.retries, options === null || options === void 0 ? void 0 : options.axiosConfig, options === null || options === void 0 ? void 0 : options.axiosInterceptors);
|
|
35
|
+
/* eslint-disable complexity */
|
|
36
|
+
return async function action(actionConfig) {
|
|
37
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
38
|
+
const { args, requestId, headers: requestHeaders, ctx: parentCtx, authArgs, userId, } = actionConfig;
|
|
39
|
+
const debugHeaders = {};
|
|
40
|
+
const lang = requestHeaders[DEFAULT_LANG_HEADER] || Lang.Ru; // header might be empty string
|
|
41
|
+
const serviceName = (options === null || options === void 0 ? void 0 : options.serviceName) || serviceKey;
|
|
42
|
+
const idempotency = config.idempotency;
|
|
43
|
+
const ctx = parentCtx.create(`Gateway ${serviceName} ${actionName} [rest]`, {
|
|
44
|
+
tags: {
|
|
45
|
+
action: actionName,
|
|
46
|
+
service: serviceName,
|
|
47
|
+
type: 'rest',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
ctx.log('Initiating request');
|
|
51
|
+
const validationSchema = config.validationSchema || options.validationSchema;
|
|
52
|
+
if (validationSchema) {
|
|
53
|
+
const invalidParams = validateArgs(args, validationSchema);
|
|
54
|
+
if (invalidParams) {
|
|
55
|
+
ctx.log('Invalid params', { invalidParams });
|
|
56
|
+
ctx.end();
|
|
57
|
+
return Promise.reject({
|
|
58
|
+
error: {
|
|
59
|
+
status: 400,
|
|
60
|
+
message: 'Validation failed',
|
|
61
|
+
code: 'INVALID_PARAMS',
|
|
62
|
+
details: {
|
|
63
|
+
title: 'Invalid params',
|
|
64
|
+
description: invalidParams,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
debugHeaders,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let endpointData = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpoint;
|
|
72
|
+
if (typeof config.endpoint === 'function') {
|
|
73
|
+
endpointData = config.endpoint(endpoints, args);
|
|
74
|
+
}
|
|
75
|
+
else if (config.endpoint) {
|
|
76
|
+
endpointData = _.get(endpoints, [config.endpoint]);
|
|
77
|
+
}
|
|
78
|
+
if (!endpointData) {
|
|
79
|
+
const errorText = `Gateway config error. Endpoint has been not found in service "${serviceKey}"`;
|
|
80
|
+
ctx.log(errorText, { serviceName, actionName });
|
|
81
|
+
ctx.end();
|
|
82
|
+
return Promise.reject({
|
|
83
|
+
error: {
|
|
84
|
+
status: 400,
|
|
85
|
+
code: 'ENDPOINT_NOT_FOUND',
|
|
86
|
+
message: errorText,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const actionEndpoint = isExtendedActionEndpoint(endpointData)
|
|
91
|
+
? endpointData.path
|
|
92
|
+
: endpointData;
|
|
93
|
+
const endpointAxiosConfig = isExtendedRestActionEndpoint(endpointData)
|
|
94
|
+
? endpointData.axiosConfig
|
|
95
|
+
: undefined;
|
|
96
|
+
const pathArgs = config.validationSchema
|
|
97
|
+
? encodePathParams(args)
|
|
98
|
+
: getPathArgsProxy(args, options.encodePathArgs);
|
|
99
|
+
const actionPath = typeof config.path === 'function' ? config.path(pathArgs) : config.path;
|
|
100
|
+
const actionURL = actionEndpoint + actionPath;
|
|
101
|
+
const parsedActionURL = url.parse(actionURL);
|
|
102
|
+
const proxyHeaders = [...DEFAULT_PROXY_HEADERS];
|
|
103
|
+
let actionHeaders = {
|
|
104
|
+
// It's important not to lose the port in HOST header
|
|
105
|
+
host: (_a = parsedActionURL.host) !== null && _a !== void 0 ? _a : undefined,
|
|
106
|
+
accept: 'application/json, */*',
|
|
107
|
+
'accept-encoding': 'gzip, deflate',
|
|
108
|
+
'accept-language': lang,
|
|
109
|
+
'x-gateway-version': VERSION,
|
|
110
|
+
};
|
|
111
|
+
if (typeof options.proxyHeaders === 'function') {
|
|
112
|
+
Object.assign(actionHeaders, options.proxyHeaders(Object.assign({}, requestHeaders), 'rest'));
|
|
113
|
+
}
|
|
114
|
+
else if (Array.isArray(options.proxyHeaders)) {
|
|
115
|
+
proxyHeaders.push(...options.proxyHeaders);
|
|
116
|
+
}
|
|
117
|
+
if (typeof config.proxyHeaders === 'function') {
|
|
118
|
+
Object.assign(actionHeaders, config.proxyHeaders(Object.assign({}, requestHeaders), 'rest'));
|
|
119
|
+
}
|
|
120
|
+
else if (Array.isArray(config.proxyHeaders)) {
|
|
121
|
+
proxyHeaders.push(...config.proxyHeaders);
|
|
122
|
+
}
|
|
123
|
+
for (const headerName of proxyHeaders) {
|
|
124
|
+
if (actionHeaders[headerName] === undefined) {
|
|
125
|
+
actionHeaders[headerName] = requestHeaders[headerName];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (requestId) {
|
|
129
|
+
actionHeaders['x-request-id'] = requestId;
|
|
130
|
+
}
|
|
131
|
+
if (idempotency) {
|
|
132
|
+
actionHeaders['idempotency-key'] = requestHeaders['idempotency-key'] || uuidv4();
|
|
133
|
+
}
|
|
134
|
+
const authHeaders = ((_b = config.getAuthHeaders) !== null && _b !== void 0 ? _b : options.getAuthHeaders)({
|
|
135
|
+
actionType: 'rest',
|
|
136
|
+
serviceName,
|
|
137
|
+
requestHeaders,
|
|
138
|
+
authArgs,
|
|
139
|
+
});
|
|
140
|
+
Object.assign(actionHeaders, authHeaders);
|
|
141
|
+
actionHeaders = _.omitBy(actionHeaders, _.isUndefined);
|
|
142
|
+
let params;
|
|
143
|
+
if (config.params) {
|
|
144
|
+
try {
|
|
145
|
+
params = await config.params(args, actionHeaders, { ctx });
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
handleError(ErrorConstructor, error, ctx, 'Getting config params failed');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const { body = undefined, query = undefined, headers = actionHeaders } = params !== null && params !== void 0 ? params : {};
|
|
152
|
+
let requestBody;
|
|
153
|
+
const serializer = getConfigSerializerFunction(config);
|
|
154
|
+
const preparedQuery = serializer && query ? serializer(query) : querystring.stringify(query);
|
|
155
|
+
const requestUrl = actionURL + (query ? '?' + preparedQuery : '');
|
|
156
|
+
try {
|
|
157
|
+
let encodedRequestBody = '';
|
|
158
|
+
if (body instanceof Buffer) {
|
|
159
|
+
encodedRequestBody = '[Buffer]';
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
encodedRequestBody = encodeURIComponent(typeof body === 'object' ? JSON.stringify(body) : String(body));
|
|
163
|
+
}
|
|
164
|
+
if (encodedRequestBody.length < 256) {
|
|
165
|
+
requestBody = encodedRequestBody;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
handleError(ErrorConstructor, error, ctx, 'Stringify request body failed');
|
|
170
|
+
}
|
|
171
|
+
Object.assign(debugHeaders, {
|
|
172
|
+
'x-api-request-method': config.method,
|
|
173
|
+
'x-api-request-url': requestUrl,
|
|
174
|
+
'x-api-request-body': requestBody ? requestBody : null,
|
|
175
|
+
'x-api-request-lang': lang,
|
|
176
|
+
'x-request-id': requestId,
|
|
177
|
+
'x-gateway-version': VERSION,
|
|
178
|
+
});
|
|
179
|
+
if (headers['content-type']) {
|
|
180
|
+
debugHeaders['x-api-content-type'] = headers['content-type'];
|
|
181
|
+
}
|
|
182
|
+
if (typeof options.proxyDebugHeaders === 'function') {
|
|
183
|
+
Object.assign(debugHeaders, options.proxyDebugHeaders(Object.assign({}, requestHeaders), 'rest'));
|
|
184
|
+
}
|
|
185
|
+
else if (Array.isArray(options.proxyDebugHeaders)) {
|
|
186
|
+
for (const headerName of options.proxyDebugHeaders) {
|
|
187
|
+
if (headers[headerName] !== undefined) {
|
|
188
|
+
debugHeaders[`x-gateway-${headerName}`] = headers[headerName];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const startRequestTime = Date.now();
|
|
193
|
+
let axiosClient = defaultAxiosClient;
|
|
194
|
+
const customActionTimeout = (_e = (_d = (_c = actionConfig.timeout) !== null && _c !== void 0 ? _c : config.timeout) !== null && _d !== void 0 ? _d : endpointAxiosConfig === null || endpointAxiosConfig === void 0 ? void 0 : endpointAxiosConfig.timeout) !== null && _e !== void 0 ? _e : timeout;
|
|
195
|
+
if (actionConfig.timeout || endpointAxiosConfig) {
|
|
196
|
+
const customActionAxiosConfig = Object.assign(Object.assign({}, ((options === null || options === void 0 ? void 0 : options.axiosConfig) || {})), (endpointAxiosConfig || {}));
|
|
197
|
+
axiosClient = getAxiosClient(customActionTimeout, config === null || config === void 0 ? void 0 : config.retries, customActionAxiosConfig, options === null || options === void 0 ? void 0 : options.axiosInterceptors);
|
|
198
|
+
}
|
|
199
|
+
headers['x-request-timeout'] = customActionTimeout !== null && customActionTimeout !== void 0 ? customActionTimeout : DEFAULT_TIMEOUT;
|
|
200
|
+
ctx.log('Starting request', { debugHeaders: sanitizeDebugHeaders(debugHeaders) });
|
|
201
|
+
const requestData = {
|
|
202
|
+
timestamp: startRequestTime,
|
|
203
|
+
service: serviceName,
|
|
204
|
+
action: actionName,
|
|
205
|
+
requestId,
|
|
206
|
+
requestMethod: config.method,
|
|
207
|
+
requestUrl: actionURL,
|
|
208
|
+
traceId: ((_f = ctx.getTraceId) === null || _f === void 0 ? void 0 : _f.call(ctx)) || '',
|
|
209
|
+
userId: userId || '',
|
|
210
|
+
};
|
|
211
|
+
const requestConfig = {
|
|
212
|
+
url: actionURL,
|
|
213
|
+
method: config.method,
|
|
214
|
+
data: body,
|
|
215
|
+
params: query,
|
|
216
|
+
headers: ctx ? Object.assign(Object.assign({}, ctx.getMetadata()), headers) : headers,
|
|
217
|
+
maxRedirects: config.maxRedirects,
|
|
218
|
+
};
|
|
219
|
+
if (config.paramsSerializer) {
|
|
220
|
+
Object.assign(requestConfig, {
|
|
221
|
+
paramsSerializer: config.paramsSerializer,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (config.responseType) {
|
|
225
|
+
Object.assign(requestConfig, {
|
|
226
|
+
responseType: config.responseType,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const response = await axiosClient.request(requestConfig);
|
|
231
|
+
const responseHeaders = {};
|
|
232
|
+
const endRequestTime = Date.now();
|
|
233
|
+
requestData.requestTime = endRequestTime - startRequestTime;
|
|
234
|
+
const actualResponseContentType = (_g = response.headers) === null || _g === void 0 ? void 0 : _g['Content-Type'];
|
|
235
|
+
const expectedResponseContentType = config.expectedResponseContentType || options.expectedResponseContentType;
|
|
236
|
+
if (actualResponseContentType && expectedResponseContentType) {
|
|
237
|
+
let isInvalidResponseContentType;
|
|
238
|
+
if (Array.isArray(expectedResponseContentType)) {
|
|
239
|
+
isInvalidResponseContentType = !expectedResponseContentType.includes(String(actualResponseContentType));
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
isInvalidResponseContentType =
|
|
243
|
+
expectedResponseContentType !== actualResponseContentType;
|
|
244
|
+
}
|
|
245
|
+
if (isInvalidResponseContentType) {
|
|
246
|
+
ctx.log('Invalid response content type', {
|
|
247
|
+
expectedResponseContentType,
|
|
248
|
+
actualResponseContentType,
|
|
249
|
+
});
|
|
250
|
+
ctx.end();
|
|
251
|
+
return Promise.reject({
|
|
252
|
+
error: {
|
|
253
|
+
status: 415,
|
|
254
|
+
message: 'Response content type validation failed',
|
|
255
|
+
code: 'INVALID_RESPONSE_CONTENT_TYPE',
|
|
256
|
+
details: {
|
|
257
|
+
title: 'Invalid response content type',
|
|
258
|
+
description: `Expected to get ${expectedResponseContentType} but got ${actualResponseContentType}`,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
debugHeaders,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (config.transformResponseData) {
|
|
266
|
+
try {
|
|
267
|
+
response.data = await config.transformResponseData(response.data, {
|
|
268
|
+
args,
|
|
269
|
+
ctx,
|
|
270
|
+
headers: response.headers,
|
|
271
|
+
});
|
|
272
|
+
ctx.log('Transformed response data');
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
handleError(ErrorConstructor, error, ctx, 'Transform response data failed');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (config.proxyResponseHeaders) {
|
|
279
|
+
const proxyResponseHeaders = [];
|
|
280
|
+
if (typeof config.proxyResponseHeaders === 'function') {
|
|
281
|
+
Object.assign(responseHeaders, config.proxyResponseHeaders(Object.assign({}, response.headers), 'rest'));
|
|
282
|
+
}
|
|
283
|
+
else if (Array.isArray(config.proxyResponseHeaders)) {
|
|
284
|
+
proxyResponseHeaders.push(...config.proxyResponseHeaders);
|
|
285
|
+
}
|
|
286
|
+
for (const headerName of proxyResponseHeaders) {
|
|
287
|
+
if (responseHeaders[headerName] === undefined) {
|
|
288
|
+
responseHeaders[headerName] = response.headers[headerName];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (options === null || options === void 0 ? void 0 : options.sendStats) {
|
|
293
|
+
options.sendStats(Object.assign(Object.assign({}, requestData), { responseSize: getRestResponseSize(response === null || response === void 0 ? void 0 : response.data, ctx, ErrorConstructor), restStatus: 200 }), redactSensitiveHeaders(parentCtx, headers), parentCtx, { debugHeaders: sanitizeDebugHeaders(debugHeaders) });
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
ctx.stats(Object.assign(Object.assign({}, requestData), { responseStatus: 200 }));
|
|
297
|
+
}
|
|
298
|
+
ctx.log('Request completed', { debugHeaders: sanitizeDebugHeaders(debugHeaders) });
|
|
299
|
+
ctx.end();
|
|
300
|
+
return { responseData: response.data, responseHeaders, debugHeaders };
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
let parsedError;
|
|
304
|
+
const endRequestTime = new Date().getTime();
|
|
305
|
+
requestData.requestTime = endRequestTime - startRequestTime;
|
|
306
|
+
if (error &&
|
|
307
|
+
error instanceof Object &&
|
|
308
|
+
'response' in error &&
|
|
309
|
+
config.transformResponseError) {
|
|
310
|
+
try {
|
|
311
|
+
parsedError = await config.transformResponseError(error.response, {
|
|
312
|
+
args,
|
|
313
|
+
ctx,
|
|
314
|
+
});
|
|
315
|
+
ctx.log('Transformed response error');
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
handleError(ErrorConstructor, error, ctx, 'Transform response error failed');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (!parsedError) {
|
|
322
|
+
try {
|
|
323
|
+
parsedError = parseRestError(error, lang);
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
handleError(ErrorConstructor, error, ctx, 'Error parse rest error');
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const responseStatus = _.get(parsedError, 'status') || _.get(error, 'status', 500);
|
|
330
|
+
if (options === null || options === void 0 ? void 0 : options.sendStats) {
|
|
331
|
+
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 }), redactSensitiveHeaders(parentCtx, headers), parentCtx, { debugHeaders: sanitizeDebugHeaders(debugHeaders) });
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
ctx.stats(Object.assign(Object.assign({}, requestData), { responseStatus }));
|
|
335
|
+
}
|
|
336
|
+
ctx.logError('Request failed', error, {
|
|
337
|
+
actionURL,
|
|
338
|
+
parsedError,
|
|
339
|
+
serviceName,
|
|
340
|
+
debugHeaders: sanitizeDebugHeaders(debugHeaders),
|
|
341
|
+
});
|
|
342
|
+
ctx.end();
|
|
343
|
+
return Promise.reject({
|
|
344
|
+
error: Object.assign(Object.assign({}, parsedError), { requestId }),
|
|
345
|
+
debugHeaders,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as https from 'https';
|
|
3
|
+
import * as grpc from '@grpc/grpc-js';
|
|
4
|
+
export declare const VERSION: any;
|
|
5
|
+
export declare enum Lang {
|
|
6
|
+
Ru = "ru",
|
|
7
|
+
En = "en"
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_TIMEOUT: number;
|
|
10
|
+
export declare const DEFAULT_LANG_HEADER = "accept-language";
|
|
11
|
+
export declare const DEFAULT_PROXY_HEADERS: string[];
|
|
12
|
+
export declare const DEFAULT_AXIOS_OPTIONS: {
|
|
13
|
+
maxContentLength: number;
|
|
14
|
+
httpAgent: http.Agent;
|
|
15
|
+
httpsAgent: https.Agent;
|
|
16
|
+
};
|
|
17
|
+
export declare const DEFAULT_GRPC_OPTIONS: {
|
|
18
|
+
'grpc.max_receive_message_length': number;
|
|
19
|
+
'grpc.keepalive_time_ms': number;
|
|
20
|
+
'grpc.keepalive_timeout_ms': number;
|
|
21
|
+
'grpc.keepalive_permit_without_calls': number;
|
|
22
|
+
};
|
|
23
|
+
export declare const DEFAULT_PROTO_LOADER_OPTIONS: {
|
|
24
|
+
longs: StringConstructor;
|
|
25
|
+
enums: StringConstructor;
|
|
26
|
+
defaults: boolean;
|
|
27
|
+
oneofs: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Byte sizes are taken from ECMAScript Language Specification
|
|
31
|
+
* http://www.ecma-international.org/ecma-262/5.1/
|
|
32
|
+
*/
|
|
33
|
+
export declare const ECMA_STRING_SIZE = 2;
|
|
34
|
+
export declare const ANY_ACTION_SYMBOL = "*";
|
|
35
|
+
export declare const RETRYABLE_STATUS_CODES: Array<grpc.status | undefined>;
|
|
36
|
+
export declare const RECREATE_SERVICE_CODES: Array<grpc.status | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Common validation schema for action's parameters. You can use it in GatewayConfig as validationSchema
|
|
39
|
+
* Validates the parameter type: only number, object, or string are allowed.
|
|
40
|
+
* For strings, disallows characters: ".", "?", "#", "/", "\"
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_VALIDATION_SCHEMA: {
|
|
43
|
+
additionalProperties: {
|
|
44
|
+
oneOf: ({
|
|
45
|
+
type: string;
|
|
46
|
+
pattern?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
type: string;
|
|
49
|
+
pattern: string;
|
|
50
|
+
})[];
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export declare const AXIOS_RETRY_NAMESPACE = "axios-retry";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as https from 'https';
|
|
3
|
+
import * as grpc from '@grpc/grpc-js';
|
|
4
|
+
const packageJson = require('../package.json');
|
|
5
|
+
export const VERSION = packageJson.version;
|
|
6
|
+
export var Lang;
|
|
7
|
+
(function (Lang) {
|
|
8
|
+
Lang["Ru"] = "ru";
|
|
9
|
+
Lang["En"] = "en";
|
|
10
|
+
})(Lang || (Lang = {}));
|
|
11
|
+
export const DEFAULT_TIMEOUT = 25 * 1000;
|
|
12
|
+
export const DEFAULT_LANG_HEADER = 'accept-language';
|
|
13
|
+
export const DEFAULT_PROXY_HEADERS = [
|
|
14
|
+
'origin',
|
|
15
|
+
'user-agent',
|
|
16
|
+
'referer',
|
|
17
|
+
'x-real-ip',
|
|
18
|
+
'x-forwarded-for',
|
|
19
|
+
];
|
|
20
|
+
export const DEFAULT_AXIOS_OPTIONS = {
|
|
21
|
+
maxContentLength: 1024 * 1024 * 100, // 100 Mb
|
|
22
|
+
httpAgent: new http.Agent({
|
|
23
|
+
// https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L233
|
|
24
|
+
family: 6,
|
|
25
|
+
}),
|
|
26
|
+
httpsAgent: new https.Agent({
|
|
27
|
+
// https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L233
|
|
28
|
+
family: 6,
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
export const DEFAULT_GRPC_OPTIONS = {
|
|
32
|
+
'grpc.max_receive_message_length': 1024 * 1024 * 100, // 100 Mb
|
|
33
|
+
'grpc.keepalive_time_ms': 10000,
|
|
34
|
+
'grpc.keepalive_timeout_ms': 1000,
|
|
35
|
+
'grpc.keepalive_permit_without_calls': 1,
|
|
36
|
+
};
|
|
37
|
+
export const DEFAULT_PROTO_LOADER_OPTIONS = {
|
|
38
|
+
longs: String,
|
|
39
|
+
enums: String,
|
|
40
|
+
defaults: true,
|
|
41
|
+
oneofs: true,
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Byte sizes are taken from ECMAScript Language Specification
|
|
45
|
+
* http://www.ecma-international.org/ecma-262/5.1/
|
|
46
|
+
*/
|
|
47
|
+
export const ECMA_STRING_SIZE = 2;
|
|
48
|
+
export const ANY_ACTION_SYMBOL = '*';
|
|
49
|
+
export const RETRYABLE_STATUS_CODES = [
|
|
50
|
+
grpc.status.UNAVAILABLE,
|
|
51
|
+
grpc.status.CANCELLED,
|
|
52
|
+
grpc.status.ABORTED,
|
|
53
|
+
grpc.status.UNKNOWN,
|
|
54
|
+
];
|
|
55
|
+
export const RECREATE_SERVICE_CODES = [
|
|
56
|
+
grpc.status.DEADLINE_EXCEEDED,
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Common validation schema for action's parameters. You can use it in GatewayConfig as validationSchema
|
|
60
|
+
* Validates the parameter type: only number, object, or string are allowed.
|
|
61
|
+
* For strings, disallows characters: ".", "?", "#", "/", "\"
|
|
62
|
+
*/
|
|
63
|
+
export const DEFAULT_VALIDATION_SCHEMA = {
|
|
64
|
+
additionalProperties: {
|
|
65
|
+
oneOf: [
|
|
66
|
+
{
|
|
67
|
+
type: 'number',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'string',
|
|
71
|
+
pattern: '^((?!(\\.\\.|\\?|#|\\\\|\\/)).)*$',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'object',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
export const AXIOS_RETRY_NAMESPACE = 'axios-retry';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApiWithRoot, GatewayConfig, GatewayRequest, GatewayResponse, SchemasByScope } from './models/common.js';
|
|
2
|
+
import { GatewayContext } from './models/context.js';
|
|
3
|
+
export * from './utils/typed-api.js';
|
|
4
|
+
export * from './utils/grpc-reflection.js';
|
|
5
|
+
export * from './models/common.js';
|
|
6
|
+
export * from './models/context.js';
|
|
7
|
+
export * from './models/error.js';
|
|
8
|
+
export declare function getGatewayControllers<TSchema extends SchemasByScope, Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse>(schemasByScope: TSchema, config: GatewayConfig<Context, Req, Res>): {
|
|
9
|
+
controller: (req: Req, res: Res) => Promise<any>;
|
|
10
|
+
api: ApiWithRoot<TSchema, Context, Req, Res>;
|
|
11
|
+
};
|
|
12
|
+
export default getGatewayControllers;
|