@gravity-ui/gateway 4.6.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.
- package/README.md +179 -3
- package/{build → dist/commonjs}/components/grpc.d.ts +5 -5
- package/{build → dist/commonjs}/components/grpc.js +109 -90
- 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 +42 -35
- package/{build → dist/commonjs}/constants.d.ts +2 -2
- package/{build → dist/commonjs}/constants.js +29 -19
- package/{build → dist/commonjs}/index.d.ts +9 -8
- package/{build → dist/commonjs}/index.js +35 -46
- package/{build → dist/commonjs}/models/common.d.ts +19 -8
- package/{build → dist/commonjs}/models/context.d.ts +0 -1
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/utils/axios.d.ts +4 -0
- package/{build → dist/commonjs}/utils/axios.js +3 -4
- package/{build → dist/commonjs}/utils/common.d.ts +4 -3
- package/{build → dist/commonjs}/utils/common.js +19 -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 +15 -35
- package/{build → dist/commonjs}/utils/grpc.d.ts +1 -1
- package/{build → dist/commonjs}/utils/grpc.js +10 -11
- 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/dist/commonjs/utils/package-root.d.ts +1 -0
- package/dist/commonjs/utils/package-root.js +44 -0
- package/{build → dist/commonjs}/utils/parse-error.d.ts +5 -5
- package/{build → dist/commonjs}/utils/parse-error.js +19 -19
- package/{build → dist/commonjs}/utils/proto-path-resolver.d.ts +1 -1
- package/{build → dist/commonjs}/utils/proto-path-resolver.js +24 -15
- 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 +691 -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 +357 -0
- package/dist/esm/constants.d.ts +53 -0
- package/dist/esm/constants.js +82 -0
- package/dist/esm/index.d.ts +13 -0
- package/dist/esm/index.js +274 -0
- package/dist/esm/models/common.d.ts +289 -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 +24 -0
- package/dist/esm/utils/common.d.ts +16 -0
- package/dist/esm/utils/common.js +48 -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 +72 -0
- package/dist/esm/utils/grpc.d.ts +15 -0
- package/dist/esm/utils/grpc.js +72 -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/package-root.d.ts +1 -0
- package/dist/esm/utils/package-root.js +8 -0
- package/dist/esm/utils/parse-error.d.ts +30 -0
- package/dist/esm/utils/parse-error.js +214 -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/validate.d.ts +0 -0
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)
|
|
@@ -17,6 +16,8 @@ A flexible and powerful Express controller for working with REST and gRPC APIs i
|
|
|
17
16
|
- [Error Handling](#error-handling)
|
|
18
17
|
- [gRPC Reflection](#grpc-reflection-for-grpc-actions)
|
|
19
18
|
- [Retryable Errors](#retryable-errors)
|
|
19
|
+
- [Request Cancellation](#request-cancellation)
|
|
20
|
+
- [Response Content Type Validation](#response-content-type-validation)
|
|
20
21
|
- [Development](#development)
|
|
21
22
|
- [Running Tests](#running-tests)
|
|
22
23
|
- [Contributing](#contributing)
|
|
@@ -73,11 +74,14 @@ interface Stats {
|
|
|
73
74
|
action: string;
|
|
74
75
|
restStatus: number;
|
|
75
76
|
grpcStatus?: number;
|
|
77
|
+
responseSize: number;
|
|
76
78
|
requestId: string;
|
|
77
79
|
requestTime: number;
|
|
78
80
|
requestMethod: string;
|
|
79
81
|
requestUrl: string;
|
|
80
82
|
timestamp: number;
|
|
83
|
+
userId?: string;
|
|
84
|
+
traceId: string;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
type SendStats = (
|
|
@@ -92,9 +96,18 @@ type AxiosRetryCondition = IAxiosRetryConfig['retryCondition'];
|
|
|
92
96
|
|
|
93
97
|
type ControllerType = 'rest' | 'grpc';
|
|
94
98
|
|
|
99
|
+
type ProxyHeadersFunctionExtra = {
|
|
100
|
+
service: string;
|
|
101
|
+
action: string;
|
|
102
|
+
|
|
103
|
+
protopath?: string;
|
|
104
|
+
protokey?: string;
|
|
105
|
+
};
|
|
106
|
+
|
|
95
107
|
type ProxyHeadersFunction = (
|
|
96
108
|
headers: IncomingHttpHeaders,
|
|
97
109
|
type: ControllerType,
|
|
110
|
+
extra: ProxyHeadersFunctionExtra,
|
|
98
111
|
) => IncomingHttpHeaders;
|
|
99
112
|
type ProxyHeaders = string[] | ProxyHeadersFunction;
|
|
100
113
|
type ResponseContentType = AxiosResponse['headers']['Content-Type'];
|
|
@@ -172,7 +185,7 @@ interface GatewayConfig {
|
|
|
172
185
|
|
|
173
186
|
// When passing a boolean value, it enables/disables debug headers in the response to the request.
|
|
174
187
|
// For unary requests to gRPC backends, debug headers will include information from the trailing metadata returned by the backend.
|
|
175
|
-
withDebugHeaders?: boolean;
|
|
188
|
+
withDebugHeaders?: boolean | ((req: Request, res: Response) => boolean);
|
|
176
189
|
|
|
177
190
|
// Validation schema for parameters used when no schema is present in the action.
|
|
178
191
|
// You can use DEFAULT_VALIDATION_SCHEMA from lib/constants.ts.
|
|
@@ -202,9 +215,63 @@ interface GatewayConfig {
|
|
|
202
215
|
|
|
203
216
|
// Error constructor for handling errors
|
|
204
217
|
ErrorConstructor: AppErrorConstructor;
|
|
218
|
+
|
|
219
|
+
// Axios interceptors configuration
|
|
220
|
+
axiosInterceptors?: AxiosInterceptorsConfig;
|
|
205
221
|
}
|
|
206
222
|
```
|
|
207
223
|
|
|
224
|
+
### `proxyHeaders`
|
|
225
|
+
|
|
226
|
+
`GatewayConfig.proxyHeaders` is an optional method that allows setting headers for requests at the entire `gateway` level:
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
const proxyHeaders = (headers, actionType, extra) => {
|
|
230
|
+
const normalizedHeaders = {...headers};
|
|
231
|
+
const {service, action, protopath, protokey} = extra;
|
|
232
|
+
|
|
233
|
+
if (actionType === 'rest' && service === 'mail') {
|
|
234
|
+
normalizedHeaders['x-mail-service-action'] = action;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return normalizedHeaders;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const {controller: gatewayController} = getGatewayControllers(
|
|
241
|
+
{root: Schema},
|
|
242
|
+
{...config, proxyHeaders},
|
|
243
|
+
);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The `extra` parameter contains additional information about the request:
|
|
247
|
+
|
|
248
|
+
- `service`: The service name
|
|
249
|
+
- `action`: The action name
|
|
250
|
+
- `protopath`: The proto path (for gRPC actions)
|
|
251
|
+
- `protokey`: The proto key (for gRPC actions)
|
|
252
|
+
|
|
253
|
+
You can set headers for a specific action using `ApiServiceBaseActionConfig.proxyHeaders`:
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
const schema = {
|
|
257
|
+
userService: {
|
|
258
|
+
serviceName: 'users',
|
|
259
|
+
endpoints: {...},
|
|
260
|
+
actions: {
|
|
261
|
+
getProfile: {
|
|
262
|
+
path: () => '/profile',
|
|
263
|
+
method: 'GET',
|
|
264
|
+
proxyHeaders: (headers) => ({...headers, ['x-users-service-action']: 'get-profile'}),
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The `GatewayConfig.proxyHeaders` and `ApiServiceBaseActionConfig.proxyHeaders` are merged when the action is called. The strategy for merging headers is not guaranteed.
|
|
272
|
+
|
|
273
|
+
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`.
|
|
274
|
+
|
|
208
275
|
### Validation Schema
|
|
209
276
|
|
|
210
277
|
By default, for path params in REST actions, the following regexp is used: `/^((?!(\.\.|\?|#|\\|\/)).)*$/i`.
|
|
@@ -270,6 +337,8 @@ interface ApiActionConfig<Context, TRequestData> {
|
|
|
270
337
|
timeout?: number;
|
|
271
338
|
callback?: (response: TResponseData) => void;
|
|
272
339
|
authArgs?: Record<string, unknown>;
|
|
340
|
+
userId?: string;
|
|
341
|
+
abortSignal?: AbortSignal;
|
|
273
342
|
}
|
|
274
343
|
```
|
|
275
344
|
|
|
@@ -461,7 +530,7 @@ The **default** retry condition for REST-actions includes the following conditio
|
|
|
461
530
|
- Network errors (detected by `axiosRetry.isNetworkError`)
|
|
462
531
|
- Other retryable errors (detected by `axiosRetry.isRetryableError`)
|
|
463
532
|
|
|
464
|
-
You can customize retry behavior
|
|
533
|
+
You can customize retry behavior using the `axiosRetryCondition` config option:
|
|
465
534
|
|
|
466
535
|
```javascript
|
|
467
536
|
const config = {
|
|
@@ -473,6 +542,27 @@ const config = {
|
|
|
473
542
|
};
|
|
474
543
|
```
|
|
475
544
|
|
|
545
|
+
You can also set retry conditions at the action level:
|
|
546
|
+
|
|
547
|
+
```javascript
|
|
548
|
+
const schema = {
|
|
549
|
+
userService: {
|
|
550
|
+
serviceName: 'users',
|
|
551
|
+
endpoints: {...},
|
|
552
|
+
actions: {
|
|
553
|
+
getProfile: {
|
|
554
|
+
path: () => '/profile',
|
|
555
|
+
method: 'GET',
|
|
556
|
+
axiosRetryCondition: (error) => {
|
|
557
|
+
// Custom logic for this specific action
|
|
558
|
+
return error.code === 'ECONNRESET';
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
};
|
|
564
|
+
```
|
|
565
|
+
|
|
476
566
|
#### gRPC-actions
|
|
477
567
|
|
|
478
568
|
The **default** retry condition for gRPC-actions includes the certain gRPC status codes:
|
|
@@ -494,8 +584,94 @@ const config = {
|
|
|
494
584
|
};
|
|
495
585
|
```
|
|
496
586
|
|
|
587
|
+
The library exports the `isRetryableGrpcError` function that you can use to check if a gRPC error is retryable according to the default conditions:
|
|
588
|
+
|
|
589
|
+
```javascript
|
|
590
|
+
import {isRetryableGrpcError} from '@gravity-ui/gateway';
|
|
591
|
+
|
|
592
|
+
// Use in your custom retry condition
|
|
593
|
+
const customGrpcRetryCondition = (error) => {
|
|
594
|
+
return isRetryableGrpcError(error) || error.code === 'RESOURCE_EXHAUSTED';
|
|
595
|
+
};
|
|
596
|
+
```
|
|
597
|
+
|
|
497
598
|
For gRPC-requests that fail with `DEADLINE_EXCEEDED`, the service connection is recreated before retrying if config option `grpcRecreateService` is not set to `false`.
|
|
498
599
|
|
|
600
|
+
### Request Cancellation
|
|
601
|
+
|
|
602
|
+
The gateway supports cancelling requests when the client disconnects. This is useful for long-running operations where you want to avoid unnecessary processing if the client is no longer waiting for the response.
|
|
603
|
+
|
|
604
|
+
This feature is enabled by default for exported controller. For API requests, you can pass an `AbortSignal` to cancel the request:
|
|
605
|
+
|
|
606
|
+
```javascript
|
|
607
|
+
const abortController = new AbortController();
|
|
608
|
+
|
|
609
|
+
const result = await gatewayApi.serviceName.actionName({
|
|
610
|
+
authArgs: {token: 'auth-token'},
|
|
611
|
+
requestId: '123',
|
|
612
|
+
headers: {},
|
|
613
|
+
args: {param1: 'value1'},
|
|
614
|
+
ctx: context,
|
|
615
|
+
abortSignal: abortController.signal,
|
|
616
|
+
});
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
You can also control this behavior at the action level using the `abortOnClientDisconnect` option:
|
|
620
|
+
|
|
621
|
+
```javascript
|
|
622
|
+
const schema = {
|
|
623
|
+
userService: {
|
|
624
|
+
serviceName: 'users',
|
|
625
|
+
endpoints: {...},
|
|
626
|
+
actions: {
|
|
627
|
+
longRunningOperation: {
|
|
628
|
+
path: () => '/process',
|
|
629
|
+
method: 'POST',
|
|
630
|
+
abortOnClientDisconnect: true, // Enable cancellation for this action
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
},
|
|
634
|
+
};
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Response Content Type Validation
|
|
638
|
+
|
|
639
|
+
For REST actions, you can validate the content type of the response to ensure it matches your expectations. This is useful for ensuring that the API returns the expected format.
|
|
640
|
+
|
|
641
|
+
You can set the expected content type at the gateway level:
|
|
642
|
+
|
|
643
|
+
```javascript
|
|
644
|
+
const config = {
|
|
645
|
+
// ...other config options
|
|
646
|
+
expectedResponseContentType: 'application/json',
|
|
647
|
+
};
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
Or at the action level:
|
|
651
|
+
|
|
652
|
+
```javascript
|
|
653
|
+
const schema = {
|
|
654
|
+
userService: {
|
|
655
|
+
serviceName: 'users',
|
|
656
|
+
endpoints: {...},
|
|
657
|
+
actions: {
|
|
658
|
+
getProfile: {
|
|
659
|
+
path: () => '/profile',
|
|
660
|
+
method: 'GET',
|
|
661
|
+
expectedResponseContentType: 'application/json',
|
|
662
|
+
},
|
|
663
|
+
getDocument: {
|
|
664
|
+
path: () => '/document',
|
|
665
|
+
method: 'GET',
|
|
666
|
+
expectedResponseContentType: ['application/pdf', 'application/octet-stream'],
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
You can specify either a single content type or an array of acceptable content types. If the response content type doesn't match any of the expected types, an error will be thrown.
|
|
674
|
+
|
|
499
675
|
### gRPC Reflection for gRPC Actions
|
|
500
676
|
|
|
501
677
|
Instead of using gRPC proto files, you can use gRPC reflection to determine the structure of services and methods.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as grpc from '@grpc/grpc-js';
|
|
2
|
-
import
|
|
2
|
+
import protobufjs from 'protobufjs';
|
|
3
3
|
import type * as descriptor from 'protobufjs/ext/descriptor';
|
|
4
|
-
import { ApiActionConfig, ApiServiceGrpcActionConfig, EndpointsConfig, GatewayApiOptions } from '../models/common';
|
|
5
|
-
import { GatewayContext } from '../models/context';
|
|
6
|
-
import { AppErrorConstructor } from '../models/error';
|
|
4
|
+
import { ApiActionConfig, ApiServiceGrpcActionConfig, EndpointsConfig, GatewayApiOptions } from '../models/common.js';
|
|
5
|
+
import { GatewayContext } from '../models/context.js';
|
|
6
|
+
import { AppErrorConstructor } from '../models/error.js';
|
|
7
7
|
declare module 'protobufjs' {
|
|
8
8
|
interface Root {
|
|
9
9
|
toDescriptor(protoVersion: string): protobufjs.Message<descriptor.IFileDescriptorSet> & descriptor.IFileDescriptorSet;
|
|
@@ -20,5 +20,5 @@ export interface GrpcContext {
|
|
|
20
20
|
}
|
|
21
21
|
export declare function createRoot(includeGrpcPaths?: string[]): protobufjs.Root;
|
|
22
22
|
export declare function getCredentialsMap(caCertificatePath?: string | null): CredentialsMap;
|
|
23
|
-
export
|
|
23
|
+
export declare function createGrpcAction<Context extends GatewayContext>({ root, credentials }: GrpcContext, endpoints: EndpointsConfig | undefined, config: ApiServiceGrpcActionConfig<Context, any, any>, serviceKey: string, actionName: string, options: GatewayApiOptions<Context>, ErrorConstructor: AppErrorConstructor): (actionConfig: ApiActionConfig<Context, any, any>) => Promise<import("../models/common.js").GatewayActionClientStreamResponse<any> | import("../models/common.js").GatewayActionServerStreamResponse<any> | import("../models/common.js").GatewayActionDuplexStreamResponse<any> | import("../models/common.js").GatewayActionUnaryResponse<any>>;
|
|
24
24
|
export {};
|