@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.
- package/LICENSE +21 -0
- package/README.md +269 -0
- package/bin/patch.js +13 -0
- package/build/components/grpc.d.ts +24 -0
- package/build/components/grpc.js +664 -0
- package/build/components/mixed.d.ts +11 -0
- package/build/components/mixed.js +66 -0
- package/build/components/rest.d.ts +7 -0
- package/build/components/rest.js +291 -0
- package/build/constants.d.ts +46 -0
- package/build/constants.js +101 -0
- package/build/index.d.ts +11 -0
- package/build/index.js +285 -0
- package/build/models/common.d.ts +241 -0
- package/build/models/common.js +8 -0
- package/build/models/context.d.ts +22 -0
- package/build/models/context.js +2 -0
- package/build/models/error.d.ts +12 -0
- package/build/models/error.js +2 -0
- package/build/utils/axios.d.ts +2 -0
- package/build/utils/axios.js +24 -0
- package/build/utils/common.d.ts +15 -0
- package/build/utils/common.js +50 -0
- package/build/utils/create-context-api.d.ts +4 -0
- package/build/utils/create-context-api.js +45 -0
- package/build/utils/grpc-reflection.d.ts +22 -0
- package/build/utils/grpc-reflection.js +56 -0
- package/build/utils/grpc.d.ts +15 -0
- package/build/utils/grpc.js +55 -0
- package/build/utils/overrideEndpoints/index.d.ts +2 -0
- package/build/utils/overrideEndpoints/index.js +4 -0
- package/build/utils/overrideEndpoints/overrideEndpoints.d.ts +17 -0
- package/build/utils/overrideEndpoints/overrideEndpoints.js +103 -0
- package/build/utils/parse-error.d.ts +32 -0
- package/build/utils/parse-error.js +224 -0
- package/build/utils/redact-sensitive-headers.d.ts +4 -0
- package/build/utils/redact-sensitive-headers.js +16 -0
- package/build/utils/typed-api.d.ts +2 -0
- package/build/utils/typed-api.js +7 -0
- package/build/utils/validate.d.ts +4 -0
- package/build/utils/validate.js +56 -0
- package/package.json +91 -0
- package/patches/grpc-reflection-js+0.1.2.patch +87 -0
- package/patches/protobufjs+6.11.4.patch +121 -0
- package/proto/google/api/annotations.proto +31 -0
- package/proto/google/api/http.proto +291 -0
- package/proto/google/protobuf/any.proto +158 -0
- package/proto/google/protobuf/api.proto +208 -0
- package/proto/google/protobuf/compiler/plugin.proto +183 -0
- package/proto/google/protobuf/descriptor.proto +909 -0
- package/proto/google/protobuf/duration.proto +116 -0
- package/proto/google/protobuf/empty.proto +52 -0
- package/proto/google/protobuf/field_mask.proto +245 -0
- package/proto/google/protobuf/source_context.proto +48 -0
- package/proto/google/protobuf/struct.proto +95 -0
- package/proto/google/protobuf/timestamp.proto +147 -0
- package/proto/google/protobuf/type.proto +187 -0
- package/proto/google/protobuf/wrappers.proto +123 -0
- package/proto/google/rpc/README.md +5 -0
- package/proto/google/rpc/code.proto +186 -0
- package/proto/google/rpc/error_details.proto +200 -0
- package/proto/google/rpc/status.proto +92 -0
- package/proto/google/type/dayofweek.proto +51 -0
- package/proto/google/type/timeofday.proto +45 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.createMixedAction = void 0;
|
|
15
|
+
const constants_1 = require("../constants");
|
|
16
|
+
const common_1 = require("../utils/common");
|
|
17
|
+
const create_context_api_1 = require("../utils/create-context-api");
|
|
18
|
+
const parse_error_1 = require("../utils/parse-error");
|
|
19
|
+
function createMixedAction(config, api, serviceName, actionName, extra, ErrorConctructor) {
|
|
20
|
+
return async (actionConfig) => {
|
|
21
|
+
const { args } = actionConfig, context = __rest(actionConfig, ["args"]);
|
|
22
|
+
const ctx = actionConfig.ctx.create(`Gateway ${serviceName} ${actionName} [mixed]`, {
|
|
23
|
+
tags: {
|
|
24
|
+
action: actionName,
|
|
25
|
+
service: serviceName,
|
|
26
|
+
type: 'mixed',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
const contextApi = (0, create_context_api_1.generateContextApi)(api, Object.assign(Object.assign({}, context), { ctx }));
|
|
30
|
+
try {
|
|
31
|
+
const responseData = await config(contextApi, args, Object.assign({ headers: actionConfig.headers, lang: actionConfig.headers[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru, ctx }, extra));
|
|
32
|
+
ctx.log('Request completed');
|
|
33
|
+
return {
|
|
34
|
+
responseData,
|
|
35
|
+
debugHeaders: {},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
if (e instanceof Object && 'error' in e) {
|
|
40
|
+
(0, common_1.handleError)(ErrorConctructor, e, ctx, 'Request failed', {
|
|
41
|
+
actionName,
|
|
42
|
+
serviceName,
|
|
43
|
+
});
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
if (e instanceof Error) {
|
|
47
|
+
const parsedError = (0, parse_error_1.parseMixedError)(e);
|
|
48
|
+
ctx.logError('Request failed', ErrorConctructor.wrap(e), {
|
|
49
|
+
actionName,
|
|
50
|
+
serviceName,
|
|
51
|
+
parsedError,
|
|
52
|
+
});
|
|
53
|
+
throw {
|
|
54
|
+
error: parsedError,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
throw {
|
|
58
|
+
error: JSON.stringify(e),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
ctx.end();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
exports.createMixedAction = createMixedAction;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ApiActionConfig, ApiServiceRestActionConfig, EndpointsConfig, GatewayApiOptions, Headers } from '../models/common';
|
|
2
|
+
import { GatewayContext } from '../models/context';
|
|
3
|
+
import { AppErrorConstructor } from '../models/error';
|
|
4
|
+
export default function createRestAction<Context extends GatewayContext>(endpoints: EndpointsConfig | undefined, config: ApiServiceRestActionConfig<Context, any, any>, serviceKey: string, actionName: string, options: GatewayApiOptions<Context>, ErrorConctructor: AppErrorConstructor): (actionConfig: ApiActionConfig<Context, any>) => Promise<{
|
|
5
|
+
responseData: unknown;
|
|
6
|
+
debugHeaders: Headers;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const querystring_1 = __importDefault(require("querystring"));
|
|
7
|
+
const url_1 = __importDefault(require("url"));
|
|
8
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
9
|
+
const uuid_1 = require("uuid");
|
|
10
|
+
const constants_1 = require("../constants");
|
|
11
|
+
const axios_1 = require("../utils/axios");
|
|
12
|
+
const common_1 = require("../utils/common");
|
|
13
|
+
const parse_error_1 = require("../utils/parse-error");
|
|
14
|
+
const redact_sensitive_headers_1 = require("../utils/redact-sensitive-headers");
|
|
15
|
+
const validate_1 = require("../utils/validate");
|
|
16
|
+
function getRestResponseSize(data, ctx, ErrorConctructor) {
|
|
17
|
+
var _a;
|
|
18
|
+
let responseSize = 0;
|
|
19
|
+
try {
|
|
20
|
+
responseSize = constants_1.ECMA_STRING_SIZE * ((_a = JSON.stringify(data)) === null || _a === void 0 ? void 0 : _a.length);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
(0, common_1.handleError)(ErrorConctructor, error, ctx, 'Calculate response size failed');
|
|
24
|
+
}
|
|
25
|
+
return responseSize;
|
|
26
|
+
}
|
|
27
|
+
function getConfigSerializerFunction(config) {
|
|
28
|
+
if (typeof config.paramsSerializer === 'function') {
|
|
29
|
+
return config.paramsSerializer;
|
|
30
|
+
}
|
|
31
|
+
if (config.paramsSerializer && 'serialize' in config.paramsSerializer) {
|
|
32
|
+
return config.paramsSerializer.serialize;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function createRestAction(endpoints, config, serviceKey, actionName, options, ErrorConctructor) {
|
|
37
|
+
var _a;
|
|
38
|
+
const timeout = (_a = config === null || config === void 0 ? void 0 : config.timeout) !== null && _a !== void 0 ? _a : options === null || options === void 0 ? void 0 : options.timeout;
|
|
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
|
+
/* eslint-disable complexity */
|
|
41
|
+
return async function action(actionConfig) {
|
|
42
|
+
var _a, _b, _c, _d;
|
|
43
|
+
const { args, requestId, headers: requestHeaders, ctx: parentCtx, authArgs } = actionConfig;
|
|
44
|
+
const debugHeaders = {};
|
|
45
|
+
const lang = requestHeaders[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru; // header might be empty string
|
|
46
|
+
const serviceName = (options === null || options === void 0 ? void 0 : options.serviceName) || serviceKey;
|
|
47
|
+
const idempotency = config.idempotency;
|
|
48
|
+
const ctx = parentCtx.create(`Gateway ${serviceName} ${actionName} [rest]`, {
|
|
49
|
+
tags: {
|
|
50
|
+
action: actionName,
|
|
51
|
+
service: serviceName,
|
|
52
|
+
type: 'rest',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
ctx.log('Initiating request');
|
|
56
|
+
const validationSchema = config.validationSchema || options.validationSchema;
|
|
57
|
+
if (validationSchema) {
|
|
58
|
+
const invalidParams = (0, validate_1.validateArgs)(args, validationSchema);
|
|
59
|
+
if (invalidParams) {
|
|
60
|
+
ctx.log('Invalid params', { invalidParams });
|
|
61
|
+
ctx.end();
|
|
62
|
+
return Promise.reject({
|
|
63
|
+
error: {
|
|
64
|
+
status: 400,
|
|
65
|
+
message: 'Validation failed',
|
|
66
|
+
code: 'INVALID_PARAMS',
|
|
67
|
+
details: {
|
|
68
|
+
title: 'Invalid params',
|
|
69
|
+
description: invalidParams,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
debugHeaders,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
let endpointData = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpoint;
|
|
77
|
+
if (typeof config.endpoint === 'function') {
|
|
78
|
+
endpointData = config.endpoint(endpoints, args);
|
|
79
|
+
}
|
|
80
|
+
else if (config.endpoint) {
|
|
81
|
+
endpointData = lodash_1.default.get(endpoints, [config.endpoint]);
|
|
82
|
+
}
|
|
83
|
+
if (!endpointData) {
|
|
84
|
+
const errorText = `Gateway config error. Endpoint has been not found in service "${serviceKey}"`;
|
|
85
|
+
ctx.log(errorText, { serviceName, actionName });
|
|
86
|
+
ctx.end();
|
|
87
|
+
return Promise.reject({
|
|
88
|
+
error: {
|
|
89
|
+
status: 400,
|
|
90
|
+
code: 'ENDPOINT_NOT_FOUND',
|
|
91
|
+
message: errorText,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const actionEndpoint = (0, common_1.isExtendedActionEndpoint)(endpointData)
|
|
96
|
+
? endpointData.path
|
|
97
|
+
: endpointData;
|
|
98
|
+
const endpointAxiosConfig = (0, common_1.isExtendedRestActionEndpoint)(endpointData)
|
|
99
|
+
? endpointData.axiosConfig || {}
|
|
100
|
+
: {};
|
|
101
|
+
const pathArgs = config.validationSchema
|
|
102
|
+
? (0, validate_1.encodePathParams)(args)
|
|
103
|
+
: (0, validate_1.getPathArgsProxy)(args, options.encodePathArgs);
|
|
104
|
+
const actionPath = typeof config.path === 'function' ? config.path(pathArgs) : config.path;
|
|
105
|
+
const actionURL = actionEndpoint + actionPath;
|
|
106
|
+
const parsedActionURL = url_1.default.parse(actionURL);
|
|
107
|
+
const proxyHeaders = [...constants_1.DEFAULT_PROXY_HEADERS];
|
|
108
|
+
let actionHeaders = {
|
|
109
|
+
// It's important not to lose the port in HOST header
|
|
110
|
+
host: (_a = parsedActionURL.host) !== null && _a !== void 0 ? _a : undefined,
|
|
111
|
+
accept: 'application/json, */*',
|
|
112
|
+
'accept-encoding': 'gzip, deflate',
|
|
113
|
+
'accept-language': lang,
|
|
114
|
+
'x-gateway-version': constants_1.VERSION,
|
|
115
|
+
};
|
|
116
|
+
if (typeof options.proxyHeaders === 'function') {
|
|
117
|
+
Object.assign(actionHeaders, options.proxyHeaders(Object.assign({}, requestHeaders), 'rest'));
|
|
118
|
+
}
|
|
119
|
+
else if (Array.isArray(options.proxyHeaders)) {
|
|
120
|
+
proxyHeaders.push(...options.proxyHeaders);
|
|
121
|
+
}
|
|
122
|
+
if (typeof config.proxyHeaders === 'function') {
|
|
123
|
+
Object.assign(actionHeaders, config.proxyHeaders(Object.assign({}, requestHeaders), 'rest'));
|
|
124
|
+
}
|
|
125
|
+
else if (Array.isArray(config.proxyHeaders)) {
|
|
126
|
+
proxyHeaders.push(...config.proxyHeaders);
|
|
127
|
+
}
|
|
128
|
+
for (const headerName of proxyHeaders) {
|
|
129
|
+
if (actionHeaders[headerName] === undefined) {
|
|
130
|
+
actionHeaders[headerName] = requestHeaders[headerName];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (requestId) {
|
|
134
|
+
actionHeaders['x-request-id'] = requestId;
|
|
135
|
+
}
|
|
136
|
+
if (idempotency) {
|
|
137
|
+
actionHeaders['idempotency-key'] = requestHeaders['idempotency-key'] || (0, uuid_1.v4)();
|
|
138
|
+
}
|
|
139
|
+
const authHeaders = ((_b = config.getAuthHeaders) !== null && _b !== void 0 ? _b : options.getAuthHeaders)({
|
|
140
|
+
actionType: 'rest',
|
|
141
|
+
serviceName,
|
|
142
|
+
requestHeaders,
|
|
143
|
+
authArgs,
|
|
144
|
+
});
|
|
145
|
+
Object.assign(actionHeaders, authHeaders);
|
|
146
|
+
actionHeaders = lodash_1.default.omitBy(actionHeaders, lodash_1.default.isUndefined);
|
|
147
|
+
let params;
|
|
148
|
+
if (config.params) {
|
|
149
|
+
try {
|
|
150
|
+
params = await config.params(args, actionHeaders, { ctx });
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
(0, common_1.handleError)(ErrorConctructor, error, ctx, 'Getting config params failed');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const { body = undefined, query = undefined, headers = actionHeaders } = params !== null && params !== void 0 ? params : {};
|
|
157
|
+
let requestBody;
|
|
158
|
+
const serializer = getConfigSerializerFunction(config);
|
|
159
|
+
const preparedQuery = serializer && query ? serializer(query) : querystring_1.default.stringify(query);
|
|
160
|
+
const requestUrl = actionURL + (query ? '?' + preparedQuery : '');
|
|
161
|
+
try {
|
|
162
|
+
let encodedRequestBody = '';
|
|
163
|
+
if (body instanceof Buffer) {
|
|
164
|
+
encodedRequestBody = '[Buffer]';
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
encodedRequestBody = encodeURIComponent(typeof body === 'object' ? JSON.stringify(body) : String(body));
|
|
168
|
+
}
|
|
169
|
+
if (encodedRequestBody.length < 256) {
|
|
170
|
+
requestBody = encodedRequestBody;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
(0, common_1.handleError)(ErrorConctructor, error, ctx, 'Stringify request body failed');
|
|
175
|
+
}
|
|
176
|
+
Object.assign(debugHeaders, {
|
|
177
|
+
'x-api-request-method': config.method,
|
|
178
|
+
'x-api-request-url': requestUrl,
|
|
179
|
+
'x-api-request-body': requestBody ? requestBody : null,
|
|
180
|
+
'x-api-request-lang': lang,
|
|
181
|
+
'x-request-id': requestId,
|
|
182
|
+
'x-gateway-version': constants_1.VERSION,
|
|
183
|
+
});
|
|
184
|
+
if (headers['content-type']) {
|
|
185
|
+
debugHeaders['x-api-content-type'] = headers['content-type'];
|
|
186
|
+
}
|
|
187
|
+
const startRequestTime = Date.now();
|
|
188
|
+
let axiosClient = defaultAxiosClient;
|
|
189
|
+
if (actionConfig.timeout || endpointAxiosConfig) {
|
|
190
|
+
const customActionTimeout = (_c = actionConfig.timeout) !== null && _c !== void 0 ? _c : timeout;
|
|
191
|
+
const customActionAxiosConfig = Object.assign(Object.assign({}, ((options === null || options === void 0 ? void 0 : options.axiosConfig) || {})), (endpointAxiosConfig || {}));
|
|
192
|
+
axiosClient = (0, axios_1.getAxiosClient)(customActionTimeout, config === null || config === void 0 ? void 0 : config.retries, customActionAxiosConfig);
|
|
193
|
+
}
|
|
194
|
+
ctx.log('Starting request', { debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders) });
|
|
195
|
+
const requestData = {
|
|
196
|
+
timestamp: startRequestTime,
|
|
197
|
+
service: serviceName,
|
|
198
|
+
action: actionName,
|
|
199
|
+
requestId,
|
|
200
|
+
requestMethod: config.method,
|
|
201
|
+
requestUrl: actionURL,
|
|
202
|
+
};
|
|
203
|
+
const requestConfig = {
|
|
204
|
+
url: actionURL,
|
|
205
|
+
method: config.method,
|
|
206
|
+
data: body,
|
|
207
|
+
params: query,
|
|
208
|
+
headers: ctx ? Object.assign(Object.assign({}, ctx.getMetadata()), headers) : headers,
|
|
209
|
+
maxRedirects: config.maxRedirects,
|
|
210
|
+
};
|
|
211
|
+
if (config.paramsSerializer) {
|
|
212
|
+
Object.assign(requestConfig, {
|
|
213
|
+
paramsSerializer: config.paramsSerializer,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const response = await axiosClient.request(requestConfig);
|
|
218
|
+
const endRequestTime = Date.now();
|
|
219
|
+
requestData.requestTime = endRequestTime - startRequestTime;
|
|
220
|
+
if (config.transformResponseData) {
|
|
221
|
+
try {
|
|
222
|
+
response.data = await config.transformResponseData(response.data, {
|
|
223
|
+
args,
|
|
224
|
+
ctx,
|
|
225
|
+
headers: response.headers,
|
|
226
|
+
});
|
|
227
|
+
ctx.log('Transformed response data');
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
(0, common_1.handleError)(ErrorConctructor, error, ctx, 'Transform response data failed');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (options === null || options === void 0 ? void 0 : options.sendStats) {
|
|
234
|
+
options.sendStats(Object.assign(Object.assign({}, requestData), { responseSize: getRestResponseSize(response === null || response === void 0 ? void 0 : response.data, ctx, ErrorConctructor), restStatus: 200 }), (0, redact_sensitive_headers_1.redactSensitiveHeaders)(parentCtx, headers), parentCtx, { debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders) });
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
ctx.stats(Object.assign(Object.assign({}, requestData), { responseStatus: 200 }));
|
|
238
|
+
}
|
|
239
|
+
ctx.log('Request completed', { debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders) });
|
|
240
|
+
ctx.end();
|
|
241
|
+
return { responseData: response.data, debugHeaders };
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
let parsedError;
|
|
245
|
+
const endRequestTime = new Date().getTime();
|
|
246
|
+
requestData.requestTime = endRequestTime - startRequestTime;
|
|
247
|
+
if (error &&
|
|
248
|
+
error instanceof Object &&
|
|
249
|
+
'response' in error &&
|
|
250
|
+
config.transformResponseError) {
|
|
251
|
+
try {
|
|
252
|
+
parsedError = await config.transformResponseError(error.response, {
|
|
253
|
+
args,
|
|
254
|
+
ctx,
|
|
255
|
+
});
|
|
256
|
+
ctx.log('Transformed response error');
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
(0, common_1.handleError)(ErrorConctructor, error, ctx, 'Transform response error failed');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!parsedError) {
|
|
263
|
+
try {
|
|
264
|
+
parsedError = (0, parse_error_1.parseRestError)(error, lang);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
(0, common_1.handleError)(ErrorConctructor, error, ctx, 'Error parse rest error');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const responseStatus = lodash_1.default.get(parsedError, 'status') || lodash_1.default.get(error, 'status', 500);
|
|
271
|
+
if (options === null || options === void 0 ? void 0 : options.sendStats) {
|
|
272
|
+
options.sendStats(Object.assign(Object.assign({}, requestData), { responseSize: getRestResponseSize((_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.data, ctx, ErrorConctructor), restStatus: responseStatus }), (0, redact_sensitive_headers_1.redactSensitiveHeaders)(parentCtx, headers), parentCtx, { debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders) });
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
ctx.stats(Object.assign(Object.assign({}, requestData), { responseStatus }));
|
|
276
|
+
}
|
|
277
|
+
ctx.logError('Request failed', error, {
|
|
278
|
+
actionURL,
|
|
279
|
+
parsedError,
|
|
280
|
+
serviceName,
|
|
281
|
+
debugHeaders: (0, common_1.sanitizeDebugHeaders)(debugHeaders),
|
|
282
|
+
});
|
|
283
|
+
ctx.end();
|
|
284
|
+
return Promise.reject({
|
|
285
|
+
error: Object.assign(Object.assign({}, parsedError), { requestId }),
|
|
286
|
+
debugHeaders,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
exports.default = createRestAction;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import 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
|
+
/**
|
|
24
|
+
* Byte sizes are taken from ECMAScript Language Specification
|
|
25
|
+
* http://www.ecma-international.org/ecma-262/5.1/
|
|
26
|
+
*/
|
|
27
|
+
export declare const ECMA_STRING_SIZE = 2;
|
|
28
|
+
export declare const ANY_ACTION_SYMBOL = "*";
|
|
29
|
+
export declare const RETRYABLE_STATUS_CODES: Array<grpc.status | undefined>;
|
|
30
|
+
export declare const RECREATE_SERVICE_CODES: Array<grpc.status | undefined>;
|
|
31
|
+
/**
|
|
32
|
+
* Common validation schema for action's parameters. You can use it in GatewayConfig as validationSchema
|
|
33
|
+
* Validates the parameter type: only number, object, or string are allowed.
|
|
34
|
+
* For strings, disallows characters: ".", "?", "#", "/", "\"
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_VALIDATION_SCHEMA: {
|
|
37
|
+
additionalProperties: {
|
|
38
|
+
oneOf: ({
|
|
39
|
+
type: string;
|
|
40
|
+
pattern?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
type: string;
|
|
43
|
+
pattern: string;
|
|
44
|
+
})[];
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
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
|
+
const http_1 = __importDefault(require("http"));
|
|
31
|
+
const https_1 = __importDefault(require("https"));
|
|
32
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
33
|
+
const packageJson = require('../package.json');
|
|
34
|
+
exports.VERSION = packageJson.version;
|
|
35
|
+
var Lang;
|
|
36
|
+
(function (Lang) {
|
|
37
|
+
Lang["Ru"] = "ru";
|
|
38
|
+
Lang["En"] = "en";
|
|
39
|
+
})(Lang || (exports.Lang = Lang = {}));
|
|
40
|
+
exports.DEFAULT_TIMEOUT = 25 * 1000;
|
|
41
|
+
exports.DEFAULT_LANG_HEADER = 'accept-language';
|
|
42
|
+
exports.DEFAULT_PROXY_HEADERS = [
|
|
43
|
+
'origin',
|
|
44
|
+
'user-agent',
|
|
45
|
+
'referer',
|
|
46
|
+
'x-real-ip',
|
|
47
|
+
'x-forwarded-for',
|
|
48
|
+
];
|
|
49
|
+
exports.DEFAULT_AXIOS_OPTIONS = {
|
|
50
|
+
maxContentLength: 1024 * 1024 * 100,
|
|
51
|
+
httpAgent: new http_1.default.Agent({
|
|
52
|
+
//@ts-ignore https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L233
|
|
53
|
+
family: 6,
|
|
54
|
+
}),
|
|
55
|
+
httpsAgent: new https_1.default.Agent({
|
|
56
|
+
//@ts-ignore https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L233
|
|
57
|
+
family: 6,
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
exports.DEFAULT_GRPC_OPTIONS = {
|
|
61
|
+
'grpc.max_receive_message_length': 1024 * 1024 * 100,
|
|
62
|
+
'grpc.keepalive_time_ms': 10000,
|
|
63
|
+
'grpc.keepalive_timeout_ms': 1000,
|
|
64
|
+
'grpc.keepalive_permit_without_calls': 1,
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Byte sizes are taken from ECMAScript Language Specification
|
|
68
|
+
* http://www.ecma-international.org/ecma-262/5.1/
|
|
69
|
+
*/
|
|
70
|
+
exports.ECMA_STRING_SIZE = 2;
|
|
71
|
+
exports.ANY_ACTION_SYMBOL = '*';
|
|
72
|
+
exports.RETRYABLE_STATUS_CODES = [
|
|
73
|
+
grpc.status.UNAVAILABLE,
|
|
74
|
+
grpc.status.CANCELLED,
|
|
75
|
+
grpc.status.ABORTED,
|
|
76
|
+
grpc.status.UNKNOWN,
|
|
77
|
+
];
|
|
78
|
+
exports.RECREATE_SERVICE_CODES = [
|
|
79
|
+
grpc.status.DEADLINE_EXCEEDED,
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* Common validation schema for action's parameters. You can use it in GatewayConfig as validationSchema
|
|
83
|
+
* Validates the parameter type: only number, object, or string are allowed.
|
|
84
|
+
* For strings, disallows characters: ".", "?", "#", "/", "\"
|
|
85
|
+
*/
|
|
86
|
+
exports.DEFAULT_VALIDATION_SCHEMA = {
|
|
87
|
+
additionalProperties: {
|
|
88
|
+
oneOf: [
|
|
89
|
+
{
|
|
90
|
+
type: 'number',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: 'string',
|
|
94
|
+
pattern: '^((?!(\\.\\.|\\?|#|\\\\|\\/)).)*$',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
type: 'object',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
};
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ApiWithRoot, GatewayConfig, GatewayRequest, GatewayResponse, SchemasByScope } from './models/common';
|
|
2
|
+
import { GatewayContext } from './models/context';
|
|
3
|
+
export * from './utils/typed-api';
|
|
4
|
+
export * from './utils/grpc-reflection';
|
|
5
|
+
export * from './models/common';
|
|
6
|
+
export * from './models/context';
|
|
7
|
+
export * from './models/error';
|
|
8
|
+
export default 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
|
+
};
|