@gravity-ui/gateway 4.6.0 → 4.7.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 +52 -1
- package/build/components/grpc.js +16 -7
- package/build/components/rest.js +10 -3
- package/build/models/common.d.ts +7 -1
- package/build/utils/common.d.ts +2 -1
- package/build/utils/common.js +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@ A flexible and powerful Express controller for working with REST and gRPC APIs i
|
|
|
7
7
|
- [Installation](#installation)
|
|
8
8
|
- [Basic Usage](#basic-usage)
|
|
9
9
|
- [Configuration](#configuration)
|
|
10
|
-
- [Config Structure](#config-structure)
|
|
11
10
|
- [Validation Schema](#validation-schema)
|
|
12
11
|
- [Using the API in Node.js](#using-the-api-in-nodejs)
|
|
13
12
|
- [Schema Scopes](#schema-scopes)
|
|
@@ -92,9 +91,18 @@ type AxiosRetryCondition = IAxiosRetryConfig['retryCondition'];
|
|
|
92
91
|
|
|
93
92
|
type ControllerType = 'rest' | 'grpc';
|
|
94
93
|
|
|
94
|
+
type ProxyHeadersFunctionExtra = {
|
|
95
|
+
service: string;
|
|
96
|
+
action: string;
|
|
97
|
+
|
|
98
|
+
protopath?: string;
|
|
99
|
+
protokey?: string;
|
|
100
|
+
};
|
|
101
|
+
|
|
95
102
|
type ProxyHeadersFunction = (
|
|
96
103
|
headers: IncomingHttpHeaders,
|
|
97
104
|
type: ControllerType,
|
|
105
|
+
extra: ProxyHeadersFunctionExtra,
|
|
98
106
|
) => IncomingHttpHeaders;
|
|
99
107
|
type ProxyHeaders = string[] | ProxyHeadersFunction;
|
|
100
108
|
type ResponseContentType = AxiosResponse['headers']['Content-Type'];
|
|
@@ -205,6 +213,49 @@ interface GatewayConfig {
|
|
|
205
213
|
}
|
|
206
214
|
```
|
|
207
215
|
|
|
216
|
+
### `proxyHeaders`
|
|
217
|
+
|
|
218
|
+
`GatewayConfig.proxyHeaders` is an optional method that allows setting headers for requests at the entire `gateway` level:
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
const proxyHeaders = (headers, actionType, {service, action}) => {
|
|
222
|
+
const normalizedHeaders = {...headers};
|
|
223
|
+
|
|
224
|
+
if (actionType === 'rest' && service === 'mail') {
|
|
225
|
+
normalizedHeaders['x-mail-service-action'] = action;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return normalizedHeaders;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const {controller: gatewayController} = getGatewayControllers(
|
|
232
|
+
{root: Schema},
|
|
233
|
+
{...config, proxyHeaders},
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
You can set headers for a specific action using `ApiServiceBaseActionConfig.proxyHeaders`:
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
const schema = {
|
|
241
|
+
userService: {
|
|
242
|
+
serviceName: 'users',
|
|
243
|
+
endpoints: {...},
|
|
244
|
+
actions: {
|
|
245
|
+
getProfile: {
|
|
246
|
+
path: () => '/profile',
|
|
247
|
+
method: 'GET',
|
|
248
|
+
proxyHeaders: (headers) => ({...headers, ['x-users-service-action']: 'get-profile'}),
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The `GatewayConfig.proxyHeaders` and `ApiServiceBaseActionConfig.proxyHeaders` are merged when the action is called. The strategy for merging headers is not guaranteed.
|
|
256
|
+
|
|
257
|
+
It is recommended to use `GatewayConfig.proxyHeaders` for assigning headers that are common to the entire application or a large number of actions. Otherwise, it is preferable to use `ApiServiceBaseActionConfig.proxyHeaders`.
|
|
258
|
+
|
|
208
259
|
### Validation Schema
|
|
209
260
|
|
|
210
261
|
By default, for path params in REST actions, the following regexp is used: `/^((?!(\.\.|\?|#|\\|\/)).)*$/i`.
|
package/build/components/grpc.js
CHANGED
|
@@ -86,7 +86,7 @@ function decodeResponse(response, packageRoot, ctx, encodedFields = [], ErrorCon
|
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
function createMetadata({ options, actionConfig, config, params, serviceName, ctx, }) {
|
|
89
|
+
function createMetadata({ options, actionConfig, config, params, serviceName, proxyHeadersCaller, ctx, }) {
|
|
90
90
|
var _a;
|
|
91
91
|
const { headers, requestId, authArgs } = actionConfig;
|
|
92
92
|
const proxyHeaders = [...constants_1.DEFAULT_PROXY_HEADERS];
|
|
@@ -95,13 +95,13 @@ function createMetadata({ options, actionConfig, config, params, serviceName, ct
|
|
|
95
95
|
'accept-language': headers[constants_1.DEFAULT_LANG_HEADER] || constants_1.Lang.Ru,
|
|
96
96
|
};
|
|
97
97
|
if (typeof options.proxyHeaders === 'function') {
|
|
98
|
-
Object.assign(metadata, options.proxyHeaders
|
|
98
|
+
Object.assign(metadata, proxyHeadersCaller(options.proxyHeaders));
|
|
99
99
|
}
|
|
100
100
|
else if (Array.isArray(options.proxyHeaders)) {
|
|
101
101
|
proxyHeaders.push(...options.proxyHeaders);
|
|
102
102
|
}
|
|
103
103
|
if (typeof config.proxyHeaders === 'function') {
|
|
104
|
-
Object.assign(metadata, config.proxyHeaders
|
|
104
|
+
Object.assign(metadata, proxyHeadersCaller(config.proxyHeaders));
|
|
105
105
|
}
|
|
106
106
|
else if (Array.isArray(config.proxyHeaders)) {
|
|
107
107
|
proxyHeaders.push(...config.proxyHeaders);
|
|
@@ -401,7 +401,7 @@ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey,
|
|
|
401
401
|
service: serviceName,
|
|
402
402
|
action: actionName,
|
|
403
403
|
requestTime: 0,
|
|
404
|
-
requestId
|
|
404
|
+
requestId,
|
|
405
405
|
requestMethod: action,
|
|
406
406
|
requestUrl: config.protoKey,
|
|
407
407
|
traceId: ((_a = ctx.getTraceId) === null || _a === void 0 ? void 0 : _a.call(ctx)) || '',
|
|
@@ -414,11 +414,19 @@ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey,
|
|
|
414
414
|
'x-request-id': requestId,
|
|
415
415
|
'x-gateway-version': constants_1.VERSION,
|
|
416
416
|
};
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
const protopath = 'protoPath' in config ? config.protoPath : undefined;
|
|
418
|
+
if (protopath) {
|
|
419
|
+
debugHeaders['x-api-request-protopath'] = protopath;
|
|
419
420
|
}
|
|
421
|
+
let proxyHeadersExtra;
|
|
422
|
+
const proxyHeadersCaller = (proxyHeadersFunc) => {
|
|
423
|
+
if (proxyHeadersExtra === undefined) {
|
|
424
|
+
proxyHeadersExtra = (0, common_2.getProxyHeadersArgs)(serviceName, actionName, config);
|
|
425
|
+
}
|
|
426
|
+
return proxyHeadersFunc(Object.assign({}, headers), 'rest', proxyHeadersExtra);
|
|
427
|
+
};
|
|
420
428
|
if (typeof options.proxyDebugHeaders === 'function') {
|
|
421
|
-
Object.assign(debugHeaders, options.proxyDebugHeaders
|
|
429
|
+
Object.assign(debugHeaders, proxyHeadersCaller(options.proxyDebugHeaders));
|
|
422
430
|
}
|
|
423
431
|
else if (Array.isArray(options.proxyDebugHeaders)) {
|
|
424
432
|
for (const headerName of options.proxyDebugHeaders) {
|
|
@@ -506,6 +514,7 @@ function createGrpcAction({ root, credentials }, endpoints, config, serviceKey,
|
|
|
506
514
|
config,
|
|
507
515
|
params,
|
|
508
516
|
serviceName,
|
|
517
|
+
proxyHeadersCaller,
|
|
509
518
|
ctx,
|
|
510
519
|
});
|
|
511
520
|
if (!service[action]) {
|
package/build/components/rest.js
CHANGED
|
@@ -113,14 +113,21 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
|
|
|
113
113
|
'accept-language': lang,
|
|
114
114
|
'x-gateway-version': constants_1.VERSION,
|
|
115
115
|
};
|
|
116
|
+
let proxyHeadersExtra;
|
|
117
|
+
const proxyHeadersCaller = (proxyHeadersFunc) => {
|
|
118
|
+
if (proxyHeadersExtra === undefined) {
|
|
119
|
+
proxyHeadersExtra = (0, common_1.getProxyHeadersArgs)(serviceName, actionName);
|
|
120
|
+
}
|
|
121
|
+
return proxyHeadersFunc(Object.assign({}, requestHeaders), 'rest', proxyHeadersExtra);
|
|
122
|
+
};
|
|
116
123
|
if (typeof options.proxyHeaders === 'function') {
|
|
117
|
-
Object.assign(actionHeaders, options.proxyHeaders
|
|
124
|
+
Object.assign(actionHeaders, proxyHeadersCaller(options.proxyHeaders));
|
|
118
125
|
}
|
|
119
126
|
else if (Array.isArray(options.proxyHeaders)) {
|
|
120
127
|
proxyHeaders.push(...options.proxyHeaders);
|
|
121
128
|
}
|
|
122
129
|
if (typeof config.proxyHeaders === 'function') {
|
|
123
|
-
Object.assign(actionHeaders, config.proxyHeaders
|
|
130
|
+
Object.assign(actionHeaders, proxyHeadersCaller(config.proxyHeaders));
|
|
124
131
|
}
|
|
125
132
|
else if (Array.isArray(config.proxyHeaders)) {
|
|
126
133
|
proxyHeaders.push(...config.proxyHeaders);
|
|
@@ -185,7 +192,7 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
|
|
|
185
192
|
debugHeaders['x-api-content-type'] = headers['content-type'];
|
|
186
193
|
}
|
|
187
194
|
if (typeof options.proxyDebugHeaders === 'function') {
|
|
188
|
-
Object.assign(debugHeaders, options.proxyDebugHeaders
|
|
195
|
+
Object.assign(debugHeaders, proxyHeadersCaller(options.proxyDebugHeaders));
|
|
189
196
|
}
|
|
190
197
|
else if (Array.isArray(options.proxyDebugHeaders)) {
|
|
191
198
|
for (const headerName of options.proxyDebugHeaders) {
|
package/build/models/common.d.ts
CHANGED
|
@@ -68,7 +68,13 @@ export interface GatewayError {
|
|
|
68
68
|
}
|
|
69
69
|
export type GrpcRetryCondition = (error: ServiceError) => boolean;
|
|
70
70
|
export type AxiosRetryCondition = IAxiosRetryConfig['retryCondition'];
|
|
71
|
-
export type
|
|
71
|
+
export type ProxyHeadersFunctionExtra = {
|
|
72
|
+
service: string;
|
|
73
|
+
action: string;
|
|
74
|
+
protopath?: string;
|
|
75
|
+
protokey?: string;
|
|
76
|
+
};
|
|
77
|
+
export type ProxyHeadersFunction = (headers: IncomingHttpHeaders, type: ControllerType, extra: ProxyHeadersFunctionExtra) => IncomingHttpHeaders;
|
|
72
78
|
export type ProxyHeaders = string[] | ProxyHeadersFunction;
|
|
73
79
|
export type ProxyResponseHeadersFunction = (headers: Headers, type: ControllerType) => Headers;
|
|
74
80
|
export type ProxyResponseHeaders = string[] | ProxyResponseHeadersFunction;
|
package/build/utils/common.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as grpc from '@grpc/grpc-js';
|
|
2
2
|
import _ from 'lodash';
|
|
3
|
-
import { ActionEndpoint, ExtendedActionEndpoint, ExtendedGrpcActionEndpoint, ExtendedRestActionEndpoint, Headers } from '../models/common';
|
|
3
|
+
import { ActionEndpoint, ApiServiceGrpcActionConfig, ExtendedActionEndpoint, ExtendedGrpcActionEndpoint, ExtendedRestActionEndpoint, Headers, ProxyHeadersFunctionExtra } from '../models/common';
|
|
4
4
|
import { Dict, GatewayContext } from '../models/context';
|
|
5
5
|
import { AppErrorConstructor } from '../models/error';
|
|
6
6
|
export declare function isExtendedActionEndpoint(endpoint: ActionEndpoint): endpoint is ExtendedActionEndpoint;
|
|
@@ -13,3 +13,4 @@ export declare function getKeys<T extends object>(obj: T): (keyof T)[];
|
|
|
13
13
|
export declare function sanitizeDebugHeaders(debugHeaders: Headers): _.Omit<Headers, "x-api-request-body">;
|
|
14
14
|
export declare function getHeadersFromMetadata(metadata: Record<string, grpc.MetadataValue[]>, prefix?: string): Record<string, string>;
|
|
15
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> | undefined) => ProxyHeadersFunctionExtra;
|
package/build/utils/common.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.handleError = exports.getHeadersFromMetadata = exports.sanitizeDebugHeaders = exports.getKeys = exports.isExtendedRestActionEndpoint = exports.isExtendedGrpcActionEndpoint = exports.isExtendedActionEndpoint = void 0;
|
|
6
|
+
exports.getProxyHeadersArgs = exports.handleError = exports.getHeadersFromMetadata = exports.sanitizeDebugHeaders = exports.getKeys = exports.isExtendedRestActionEndpoint = exports.isExtendedGrpcActionEndpoint = exports.isExtendedActionEndpoint = void 0;
|
|
7
7
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
8
|
function isExtendedActionEndpoint(endpoint) {
|
|
9
9
|
return (endpoint === null || endpoint === void 0 ? void 0 : endpoint.path) !== undefined;
|
|
@@ -49,3 +49,14 @@ function handleError(ErrorConstructor, error, ctx, message, extra) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
exports.handleError = handleError;
|
|
52
|
+
const getProxyHeadersArgs = (serviceName, actionName, grpcConfig) => {
|
|
53
|
+
const protopath = grpcConfig && 'protoPath' in grpcConfig ? grpcConfig.protoPath : undefined;
|
|
54
|
+
const protokey = grpcConfig === null || grpcConfig === void 0 ? void 0 : grpcConfig.protoKey;
|
|
55
|
+
return {
|
|
56
|
+
service: serviceName,
|
|
57
|
+
action: actionName,
|
|
58
|
+
protopath,
|
|
59
|
+
protokey,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
exports.getProxyHeadersArgs = getProxyHeadersArgs;
|