@gravity-ui/gateway 4.7.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.
Files changed (95) hide show
  1. package/README.md +128 -3
  2. package/{build → dist/commonjs}/components/grpc.d.ts +5 -5
  3. package/{build → dist/commonjs}/components/grpc.js +94 -84
  4. package/{build → dist/commonjs}/components/mixed.d.ts +4 -4
  5. package/{build → dist/commonjs}/components/mixed.js +11 -12
  6. package/{build → dist/commonjs}/components/rest.d.ts +5 -5
  7. package/{build → dist/commonjs}/components/rest.js +33 -33
  8. package/{build → dist/commonjs}/constants.d.ts +2 -2
  9. package/{build → dist/commonjs}/constants.js +29 -19
  10. package/{build → dist/commonjs}/index.d.ts +9 -8
  11. package/{build → dist/commonjs}/index.js +35 -46
  12. package/{build → dist/commonjs}/models/common.d.ts +12 -7
  13. package/{build → dist/commonjs}/models/context.d.ts +0 -1
  14. package/dist/commonjs/package.json +3 -0
  15. package/dist/commonjs/utils/axios.d.ts +4 -0
  16. package/{build → dist/commonjs}/utils/axios.js +3 -4
  17. package/{build → dist/commonjs}/utils/common.d.ts +4 -4
  18. package/{build → dist/commonjs}/utils/common.js +8 -8
  19. package/{build → dist/commonjs}/utils/create-context-api.d.ts +2 -2
  20. package/{build → dist/commonjs}/utils/create-context-api.js +5 -6
  21. package/{build → dist/commonjs}/utils/grpc-reflection.js +15 -35
  22. package/{build → dist/commonjs}/utils/grpc.d.ts +1 -1
  23. package/{build → dist/commonjs}/utils/grpc.js +10 -11
  24. package/dist/commonjs/utils/overrideEndpoints/index.d.ts +2 -0
  25. package/dist/commonjs/utils/overrideEndpoints/index.js +4 -0
  26. package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.d.ts +1 -1
  27. package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.js +1 -2
  28. package/dist/commonjs/utils/package-root.d.ts +1 -0
  29. package/dist/commonjs/utils/package-root.js +44 -0
  30. package/{build → dist/commonjs}/utils/parse-error.d.ts +5 -5
  31. package/{build → dist/commonjs}/utils/parse-error.js +19 -19
  32. package/{build → dist/commonjs}/utils/proto-path-resolver.d.ts +1 -1
  33. package/{build → dist/commonjs}/utils/proto-path-resolver.js +24 -15
  34. package/{build → dist/commonjs}/utils/redact-sensitive-headers.d.ts +1 -2
  35. package/{build → dist/commonjs}/utils/redact-sensitive-headers.js +1 -2
  36. package/dist/commonjs/utils/source-dir.d.ts +1 -0
  37. package/dist/commonjs/utils/source-dir.js +41 -0
  38. package/{build → dist/commonjs}/utils/typed-api.d.ts +1 -1
  39. package/{build → dist/commonjs}/utils/typed-api.js +1 -2
  40. package/{build → dist/commonjs}/utils/validate.js +6 -10
  41. package/dist/esm/components/grpc.d.ts +24 -0
  42. package/dist/esm/components/grpc.js +691 -0
  43. package/dist/esm/components/mixed.d.ts +11 -0
  44. package/dist/esm/components/mixed.js +62 -0
  45. package/dist/esm/components/rest.d.ts +8 -0
  46. package/dist/esm/components/rest.js +357 -0
  47. package/dist/esm/constants.d.ts +53 -0
  48. package/dist/esm/constants.js +82 -0
  49. package/dist/esm/index.d.ts +13 -0
  50. package/dist/esm/index.js +274 -0
  51. package/dist/esm/models/common.d.ts +289 -0
  52. package/dist/esm/models/common.js +5 -0
  53. package/dist/esm/models/context.d.ts +22 -0
  54. package/dist/esm/models/context.js +1 -0
  55. package/dist/esm/models/error.d.ts +12 -0
  56. package/dist/esm/models/error.js +1 -0
  57. package/dist/esm/package.json +3 -0
  58. package/{build → dist/esm}/utils/axios.d.ts +1 -1
  59. package/dist/esm/utils/axios.js +24 -0
  60. package/dist/esm/utils/common.d.ts +16 -0
  61. package/dist/esm/utils/common.js +48 -0
  62. package/dist/esm/utils/create-context-api.d.ts +4 -0
  63. package/dist/esm/utils/create-context-api.js +38 -0
  64. package/dist/esm/utils/grpc-reflection.d.ts +28 -0
  65. package/dist/esm/utils/grpc-reflection.js +72 -0
  66. package/dist/esm/utils/grpc.d.ts +15 -0
  67. package/dist/esm/utils/grpc.js +72 -0
  68. package/dist/esm/utils/overrideEndpoints/index.d.ts +2 -0
  69. package/dist/esm/utils/overrideEndpoints/index.js +2 -0
  70. package/dist/esm/utils/overrideEndpoints/overrideEndpoints.d.ts +17 -0
  71. package/dist/esm/utils/overrideEndpoints/overrideEndpoints.js +96 -0
  72. package/dist/esm/utils/package-root.d.ts +1 -0
  73. package/dist/esm/utils/package-root.js +8 -0
  74. package/dist/esm/utils/parse-error.d.ts +30 -0
  75. package/dist/esm/utils/parse-error.js +214 -0
  76. package/dist/esm/utils/proto-path-resolver.d.ts +2 -0
  77. package/dist/esm/utils/proto-path-resolver.js +23 -0
  78. package/dist/esm/utils/redact-sensitive-headers.d.ts +3 -0
  79. package/dist/esm/utils/redact-sensitive-headers.js +12 -0
  80. package/dist/esm/utils/source-dir.d.ts +1 -0
  81. package/dist/esm/utils/source-dir.js +4 -0
  82. package/dist/esm/utils/typed-api.d.ts +2 -0
  83. package/dist/esm/utils/typed-api.js +3 -0
  84. package/dist/esm/utils/validate.d.ts +4 -0
  85. package/dist/esm/utils/validate.js +47 -0
  86. package/package.json +41 -16
  87. package/build/utils/overrideEndpoints/index.d.ts +0 -2
  88. package/build/utils/overrideEndpoints/index.js +0 -4
  89. /package/bin/{patch.js → patch.cjs} +0 -0
  90. /package/{build → dist/commonjs}/models/common.js +0 -0
  91. /package/{build → dist/commonjs}/models/context.js +0 -0
  92. /package/{build → dist/commonjs}/models/error.d.ts +0 -0
  93. /package/{build → dist/commonjs}/models/error.js +0 -0
  94. /package/{build → dist/commonjs}/utils/grpc-reflection.d.ts +0 -0
  95. /package/{build → dist/commonjs}/utils/validate.d.ts +0 -0
package/README.md CHANGED
@@ -16,6 +16,8 @@ A flexible and powerful Express controller for working with REST and gRPC APIs i
16
16
  - [Error Handling](#error-handling)
17
17
  - [gRPC Reflection](#grpc-reflection-for-grpc-actions)
18
18
  - [Retryable Errors](#retryable-errors)
19
+ - [Request Cancellation](#request-cancellation)
20
+ - [Response Content Type Validation](#response-content-type-validation)
19
21
  - [Development](#development)
20
22
  - [Running Tests](#running-tests)
21
23
  - [Contributing](#contributing)
@@ -72,11 +74,14 @@ interface Stats {
72
74
  action: string;
73
75
  restStatus: number;
74
76
  grpcStatus?: number;
77
+ responseSize: number;
75
78
  requestId: string;
76
79
  requestTime: number;
77
80
  requestMethod: string;
78
81
  requestUrl: string;
79
82
  timestamp: number;
83
+ userId?: string;
84
+ traceId: string;
80
85
  }
81
86
 
82
87
  type SendStats = (
@@ -180,7 +185,7 @@ interface GatewayConfig {
180
185
 
181
186
  // When passing a boolean value, it enables/disables debug headers in the response to the request.
182
187
  // For unary requests to gRPC backends, debug headers will include information from the trailing metadata returned by the backend.
183
- withDebugHeaders?: boolean;
188
+ withDebugHeaders?: boolean | ((req: Request, res: Response) => boolean);
184
189
 
185
190
  // Validation schema for parameters used when no schema is present in the action.
186
191
  // You can use DEFAULT_VALIDATION_SCHEMA from lib/constants.ts.
@@ -210,6 +215,9 @@ interface GatewayConfig {
210
215
 
211
216
  // Error constructor for handling errors
212
217
  ErrorConstructor: AppErrorConstructor;
218
+
219
+ // Axios interceptors configuration
220
+ axiosInterceptors?: AxiosInterceptorsConfig;
213
221
  }
214
222
  ```
215
223
 
@@ -218,8 +226,9 @@ interface GatewayConfig {
218
226
  `GatewayConfig.proxyHeaders` is an optional method that allows setting headers for requests at the entire `gateway` level:
219
227
 
220
228
  ```javascript
221
- const proxyHeaders = (headers, actionType, {service, action}) => {
229
+ const proxyHeaders = (headers, actionType, extra) => {
222
230
  const normalizedHeaders = {...headers};
231
+ const {service, action, protopath, protokey} = extra;
223
232
 
224
233
  if (actionType === 'rest' && service === 'mail') {
225
234
  normalizedHeaders['x-mail-service-action'] = action;
@@ -234,6 +243,13 @@ const {controller: gatewayController} = getGatewayControllers(
234
243
  );
235
244
  ```
236
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
+
237
253
  You can set headers for a specific action using `ApiServiceBaseActionConfig.proxyHeaders`:
238
254
 
239
255
  ```javascript
@@ -321,6 +337,8 @@ interface ApiActionConfig<Context, TRequestData> {
321
337
  timeout?: number;
322
338
  callback?: (response: TResponseData) => void;
323
339
  authArgs?: Record<string, unknown>;
340
+ userId?: string;
341
+ abortSignal?: AbortSignal;
324
342
  }
325
343
  ```
326
344
 
@@ -512,7 +530,7 @@ The **default** retry condition for REST-actions includes the following conditio
512
530
  - Network errors (detected by `axiosRetry.isNetworkError`)
513
531
  - Other retryable errors (detected by `axiosRetry.isRetryableError`)
514
532
 
515
- You can customize retry behavior for using the `axiosRetryCondition` config option:
533
+ You can customize retry behavior using the `axiosRetryCondition` config option:
516
534
 
517
535
  ```javascript
518
536
  const config = {
@@ -524,6 +542,27 @@ const config = {
524
542
  };
525
543
  ```
526
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
+
527
566
  #### gRPC-actions
528
567
 
529
568
  The **default** retry condition for gRPC-actions includes the certain gRPC status codes:
@@ -545,8 +584,94 @@ const config = {
545
584
  };
546
585
  ```
547
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
+
548
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`.
549
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
+
550
675
  ### gRPC Reflection for gRPC Actions
551
676
 
552
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 * as protobufjs from 'protobufjs';
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 default 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").GatewayActionClientStreamResponse<any> | import("../models/common").GatewayActionServerStreamResponse<any> | import("../models/common").GatewayActionDuplexStreamResponse<any> | import("../models/common").GatewayActionUnaryResponse<any>>;
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 {};