@gravity-ui/gateway 2.2.0 → 2.4.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/README.md +2 -1
- package/build/components/grpc.js +10 -0
- package/build/components/rest.js +37 -19
- package/build/index.js +2 -0
- package/build/models/common.d.ts +7 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ type ProxyHeadersFunction = (
|
|
|
72
72
|
type: ControllerType,
|
|
73
73
|
) => IncomingHttpHeaders;
|
|
74
74
|
type ProxyHeaders = string[] | ProxyHeadersFunction;
|
|
75
|
+
type ResponseContentType = AxiosResponse['headers']['Content-Type'];
|
|
75
76
|
|
|
76
77
|
interface GatewayConfig {
|
|
77
78
|
// Gateway Installation (external/internal/...). If the configuration is not provided, it is determined from process.env.APP_INSTALLATION.
|
|
@@ -119,7 +120,7 @@ interface GatewayConfig {
|
|
|
119
120
|
// Configuration for automatic connection re-establishment upon connection error through L3 load balancer (default is true).
|
|
120
121
|
grpcRecreateService?: boolean;
|
|
121
122
|
// Enable verification of response contentType header. Actual only for REST actions. This value can be set / redefined the in action confg.
|
|
122
|
-
expectedResponseContentType?:
|
|
123
|
+
expectedResponseContentType?: ResponseContentType | ResponseContentType[];
|
|
123
124
|
}
|
|
124
125
|
```
|
|
125
126
|
|
package/build/components/grpc.js
CHANGED
|
@@ -426,6 +426,16 @@ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey,
|
|
|
426
426
|
if ('protoPath' in config) {
|
|
427
427
|
debugHeaders['x-api-request-protopath'] = config.protoPath;
|
|
428
428
|
}
|
|
429
|
+
if (typeof options.proxyDebugHeaders === 'function') {
|
|
430
|
+
Object.assign(debugHeaders, options.proxyDebugHeaders(Object.assign({}, headers), 'grpc'));
|
|
431
|
+
}
|
|
432
|
+
else if (Array.isArray(options.proxyDebugHeaders)) {
|
|
433
|
+
for (const headerName of options.proxyDebugHeaders) {
|
|
434
|
+
if (headers[headerName] !== undefined) {
|
|
435
|
+
debugHeaders[`x-gateway-${headerName}`] = headers[headerName];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
429
439
|
ctx.log('Initiating request', { debugHeaders: (0, common_2.sanitizeDebugHeaders)(debugHeaders) });
|
|
430
440
|
const sendStats = (status, data) => {
|
|
431
441
|
if (options === null || options === void 0 ? void 0 : options.sendStats) {
|
package/build/components/rest.js
CHANGED
|
@@ -184,6 +184,16 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
|
|
|
184
184
|
if (headers['content-type']) {
|
|
185
185
|
debugHeaders['x-api-content-type'] = headers['content-type'];
|
|
186
186
|
}
|
|
187
|
+
if (typeof options.proxyDebugHeaders === 'function') {
|
|
188
|
+
Object.assign(debugHeaders, options.proxyDebugHeaders(Object.assign({}, requestHeaders), 'rest'));
|
|
189
|
+
}
|
|
190
|
+
else if (Array.isArray(options.proxyDebugHeaders)) {
|
|
191
|
+
for (const headerName of options.proxyDebugHeaders) {
|
|
192
|
+
if (headers[headerName] !== undefined) {
|
|
193
|
+
debugHeaders[`x-gateway-${headerName}`] = headers[headerName];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
187
197
|
const startRequestTime = Date.now();
|
|
188
198
|
let axiosClient = defaultAxiosClient;
|
|
189
199
|
if (actionConfig.timeout || endpointAxiosConfig) {
|
|
@@ -225,26 +235,34 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
|
|
|
225
235
|
requestData.requestTime = endRequestTime - startRequestTime;
|
|
226
236
|
const actualResponseContentType = (_f = response.headers) === null || _f === void 0 ? void 0 : _f['Content-Type'];
|
|
227
237
|
const expectedResponseContentType = config.expectedResponseContentType || options.expectedResponseContentType;
|
|
228
|
-
if (actualResponseContentType &&
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
if (actualResponseContentType && expectedResponseContentType) {
|
|
239
|
+
let isInvalidResponseContentType;
|
|
240
|
+
if (Array.isArray(expectedResponseContentType)) {
|
|
241
|
+
isInvalidResponseContentType = !expectedResponseContentType.includes(String(actualResponseContentType));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
isInvalidResponseContentType =
|
|
245
|
+
expectedResponseContentType !== actualResponseContentType;
|
|
246
|
+
}
|
|
247
|
+
if (isInvalidResponseContentType) {
|
|
248
|
+
ctx.log('Invalid response content type', {
|
|
249
|
+
expectedResponseContentType,
|
|
250
|
+
actualResponseContentType,
|
|
251
|
+
});
|
|
252
|
+
ctx.end();
|
|
253
|
+
return Promise.reject({
|
|
254
|
+
error: {
|
|
255
|
+
status: 415,
|
|
256
|
+
message: 'Response content type validation failed',
|
|
257
|
+
code: 'INVALID_RESPONSE_CONTENT_TYPE',
|
|
258
|
+
details: {
|
|
259
|
+
title: 'Invalid response content type',
|
|
260
|
+
description: `Expected to get ${expectedResponseContentType} but got ${actualResponseContentType}`,
|
|
261
|
+
},
|
|
244
262
|
},
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
263
|
+
debugHeaders,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
248
266
|
}
|
|
249
267
|
if (config.transformResponseData) {
|
|
250
268
|
try {
|
package/build/index.js
CHANGED
|
@@ -75,6 +75,7 @@ function createApiAction(schema, config, serviceKey, actionName, api, grpcContex
|
|
|
75
75
|
timeout: config.timeout,
|
|
76
76
|
sendStats: config.sendStats,
|
|
77
77
|
proxyHeaders: config.proxyHeaders,
|
|
78
|
+
proxyDebugHeaders: config.proxyDebugHeaders,
|
|
78
79
|
axiosConfig: config.axiosConfig,
|
|
79
80
|
validationSchema: config.validationSchema,
|
|
80
81
|
encodePathArgs: config.encodePathArgs,
|
|
@@ -87,6 +88,7 @@ function createApiAction(schema, config, serviceKey, actionName, api, grpcContex
|
|
|
87
88
|
timeout: config.timeout,
|
|
88
89
|
sendStats: config.sendStats,
|
|
89
90
|
proxyHeaders: config.proxyHeaders,
|
|
91
|
+
proxyDebugHeaders: config.proxyDebugHeaders,
|
|
90
92
|
grpcOptions: config.grpcOptions,
|
|
91
93
|
grpcRecreateService,
|
|
92
94
|
getAuthHeaders: config.getAuthHeaders,
|
package/build/models/common.d.ts
CHANGED
|
@@ -65,12 +65,13 @@ export type ProxyHeaders = string[] | ProxyHeadersFunction;
|
|
|
65
65
|
export type ProxyResponseHeadersFunction = (headers: Headers, type: ControllerType) => Headers;
|
|
66
66
|
export type ProxyResponseHeaders = string[] | ProxyResponseHeadersFunction;
|
|
67
67
|
export type GetAuthHeadersParams<AuthArgs = Record<string, unknown>> = {
|
|
68
|
-
actionType:
|
|
68
|
+
actionType: ControllerType;
|
|
69
69
|
serviceName: string;
|
|
70
70
|
requestHeaders: Headers;
|
|
71
71
|
authArgs: AuthArgs | undefined;
|
|
72
72
|
};
|
|
73
73
|
export type GetAuthHeaders<AuthArgs = Record<string, unknown>> = (params: GetAuthHeadersParams<AuthArgs>) => Record<string, string> | undefined;
|
|
74
|
+
export type ResponseContentType = AxiosResponse['headers']['Content-Type'];
|
|
74
75
|
export interface GatewayApiOptions<Context extends GatewayContext> {
|
|
75
76
|
serviceName: string;
|
|
76
77
|
timeout?: number;
|
|
@@ -79,9 +80,10 @@ export interface GatewayApiOptions<Context extends GatewayContext> {
|
|
|
79
80
|
grpcRecreateService?: boolean;
|
|
80
81
|
axiosConfig?: AxiosRequestConfig;
|
|
81
82
|
proxyHeaders?: ProxyHeaders;
|
|
83
|
+
proxyDebugHeaders?: ProxyHeaders;
|
|
82
84
|
validationSchema?: object;
|
|
83
85
|
encodePathArgs?: boolean;
|
|
84
|
-
expectedResponseContentType?:
|
|
86
|
+
expectedResponseContentType?: ResponseContentType | ResponseContentType[];
|
|
85
87
|
getAuthHeaders: GetAuthHeaders;
|
|
86
88
|
}
|
|
87
89
|
export interface ParamsOutput {
|
|
@@ -125,7 +127,7 @@ export interface ApiServiceRestActionConfig<Context extends GatewayContext, TOut
|
|
|
125
127
|
path: (args: TParams) => string;
|
|
126
128
|
paramsSerializer?: AxiosRequestConfig['paramsSerializer'];
|
|
127
129
|
responseType?: AxiosRequestConfig['responseType'];
|
|
128
|
-
expectedResponseContentType?:
|
|
130
|
+
expectedResponseContentType?: ResponseContentType | ResponseContentType[];
|
|
129
131
|
maxRedirects?: number;
|
|
130
132
|
}
|
|
131
133
|
export interface ApiServiceBaseGrpcActionConfig<Context extends GatewayContext, TOutput, TParams = undefined, TTransformed = TOutput> extends ApiServiceBaseActionConfig<Context, TOutput, TParams, TTransformed> {
|
|
@@ -238,7 +240,8 @@ export interface GatewayConfig<Context extends GatewayContext, Req extends Gatew
|
|
|
238
240
|
includeProtoRoots?: string[];
|
|
239
241
|
caCertificatePath: string | null;
|
|
240
242
|
proxyHeaders: ProxyHeaders;
|
|
241
|
-
|
|
243
|
+
proxyDebugHeaders?: ProxyHeaders;
|
|
244
|
+
withDebugHeaders?: boolean | ((req: Req, res: Res) => boolean);
|
|
242
245
|
validationSchema?: object;
|
|
243
246
|
encodePathArgs?: boolean;
|
|
244
247
|
getAuthArgs: (req: Req, res: Res) => Record<string, unknown> | undefined;
|