@nest-yalc-2/api-strategy 1.3.3
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 +661 -0
- package/README.md +40 -0
- package/package.json +33 -0
- package/src/__tests__/call-strategy-wrappers.spec.ts +217 -0
- package/src/__tests__/context-call.service.spec.ts +52 -0
- package/src/__tests__/context-event.service.spec.ts +52 -0
- package/src/__tests__/event-strategy-composition.spec.ts +185 -0
- package/src/__tests__/header-whitelist.helper.spec.ts +30 -0
- package/src/__tests__/http-abstract-call.strategy.spec.ts +17 -0
- package/src/__tests__/nest-http-call.strategy.spec.ts +164 -0
- package/src/__tests__/nest-local-call.strategy.spec.ts +263 -0
- package/src/__tests__/nest-local-event.strategy.spec.ts +55 -0
- package/src/__tests__/rabbitmq-event.strategy.spec.ts +220 -0
- package/src/__tests__/strategy-selector.provider.spec.ts +182 -0
- package/src/context-call.interface.d.ts +12 -0
- package/src/context-call.interface.js +3 -0
- package/src/context-call.interface.js.map +1 -0
- package/src/context-call.interface.ts +49 -0
- package/src/context-call.service.d.ts +7 -0
- package/src/context-call.service.js +32 -0
- package/src/context-call.service.js.map +1 -0
- package/src/context-call.service.ts +31 -0
- package/src/context-event.interface.d.ts +4 -0
- package/src/context-event.interface.js +3 -0
- package/src/context-event.interface.js.map +1 -0
- package/src/context-event.interface.ts +4 -0
- package/src/context-event.service.d.ts +7 -0
- package/src/context-event.service.js +32 -0
- package/src/context-event.service.js.map +1 -0
- package/src/context-event.service.ts +31 -0
- package/src/header-whitelist.helper.d.ts +2 -0
- package/src/header-whitelist.helper.js +13 -0
- package/src/header-whitelist.helper.js.map +1 -0
- package/src/header-whitelist.helper.ts +12 -0
- package/src/index.d.ts +15 -0
- package/src/index.js +32 -0
- package/src/index.js.map +1 -0
- package/src/index.ts +17 -0
- package/src/strategies/composite-event.strategy.d.ts +14 -0
- package/src/strategies/composite-event.strategy.js +52 -0
- package/src/strategies/composite-event.strategy.js.map +1 -0
- package/src/strategies/composite-event.strategy.ts +76 -0
- package/src/strategies/conditional-call.strategy.d.ts +17 -0
- package/src/strategies/conditional-call.strategy.js +41 -0
- package/src/strategies/conditional-call.strategy.js.map +1 -0
- package/src/strategies/conditional-call.strategy.ts +70 -0
- package/src/strategies/conditional-event.strategy.d.ts +16 -0
- package/src/strategies/conditional-event.strategy.js +38 -0
- package/src/strategies/conditional-event.strategy.js.map +1 -0
- package/src/strategies/conditional-event.strategy.ts +54 -0
- package/src/strategies/fallback-call.strategy.d.ts +14 -0
- package/src/strategies/fallback-call.strategy.js +41 -0
- package/src/strategies/fallback-call.strategy.js.map +1 -0
- package/src/strategies/fallback-call.strategy.ts +67 -0
- package/src/strategies/http-abstract-call.strategy.d.ts +44 -0
- package/src/strategies/http-abstract-call.strategy.js +19 -0
- package/src/strategies/http-abstract-call.strategy.js.map +1 -0
- package/src/strategies/http-abstract-call.strategy.ts +238 -0
- package/src/strategies/nest-http-call.strategy.d.ts +30 -0
- package/src/strategies/nest-http-call.strategy.js +68 -0
- package/src/strategies/nest-http-call.strategy.js.map +1 -0
- package/src/strategies/nest-http-call.strategy.ts +121 -0
- package/src/strategies/nest-local-call.strategy.d.ts +32 -0
- package/src/strategies/nest-local-call.strategy.js +84 -0
- package/src/strategies/nest-local-call.strategy.js.map +1 -0
- package/src/strategies/nest-local-call.strategy.ts +153 -0
- package/src/strategies/nest-local-event.strategy.d.ts +17 -0
- package/src/strategies/nest-local-event.strategy.js +30 -0
- package/src/strategies/nest-local-event.strategy.js.map +1 -0
- package/src/strategies/nest-local-event.strategy.ts +42 -0
- package/src/strategies/rabbitmq-event.strategy.d.ts +34 -0
- package/src/strategies/rabbitmq-event.strategy.js +97 -0
- package/src/strategies/rabbitmq-event.strategy.js.map +1 -0
- package/src/strategies/rabbitmq-event.strategy.ts +154 -0
- package/src/strategies/shadow-call.strategy.d.ts +17 -0
- package/src/strategies/shadow-call.strategy.js +42 -0
- package/src/strategies/shadow-call.strategy.js.map +1 -0
- package/src/strategies/shadow-call.strategy.ts +87 -0
- package/src/strategy-selector.provider.d.ts +19 -0
- package/src/strategy-selector.provider.js +52 -0
- package/src/strategy-selector.provider.js.map +1 -0
- package/src/strategy-selector.provider.ts +103 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { HTTPMethods } from '@nest-yalc-2/types/globals.d.js';
|
|
2
|
+
import { OutgoingHttpHeaders, IncomingHttpHeaders } from 'node:http2';
|
|
3
|
+
import { IApiCallStrategy } from '../context-call.interface.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for HTTP call strategies
|
|
7
|
+
*/
|
|
8
|
+
export interface IHttpCallStrategyOptions {
|
|
9
|
+
/**
|
|
10
|
+
* List of header names that should be forwarded from the original request context.
|
|
11
|
+
* Only headers matching these names will be included in outgoing requests.
|
|
12
|
+
* @example ['authorization', 'x-tenant-id', 'content-type']
|
|
13
|
+
*/
|
|
14
|
+
headersWhitelist?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* Optional function to determine if JSON parsing should be skipped for a response body.
|
|
17
|
+
* Useful for handling non-JSON responses or streaming data.
|
|
18
|
+
* @param body - The raw response body as a string
|
|
19
|
+
* @returns `true` to skip parsing and return the raw body, `false` to parse as JSON
|
|
20
|
+
* @example (body) => body.startsWith('<html>')
|
|
21
|
+
*/
|
|
22
|
+
shouldSkipJsonParse?: (body: string) => boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* HTTP request options compliant with all HTTP-based call strategies.
|
|
27
|
+
* This interface provides a unified way to configure HTTP requests across different transport mechanisms.
|
|
28
|
+
* @template TData - The type of data being sent in the request body (e.g., object, string, Buffer, stream)
|
|
29
|
+
* @template TParams - The shape of URL query parameters as a key-value record
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // For a POST request with JSON body
|
|
33
|
+
* const options: HttpOptions<{ userId: number }, { includeDetails: string }> = {
|
|
34
|
+
* method: 'POST',
|
|
35
|
+
* data: { userId: 123 },
|
|
36
|
+
* parameters: { includeDetails: 'true' }
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface HttpOptions<
|
|
41
|
+
TData = string | object | Buffer | NodeJS.ReadableStream,
|
|
42
|
+
TParams extends Record<string, any> = Record<string, any>,
|
|
43
|
+
> {
|
|
44
|
+
/** HTTP headers to include in the request */
|
|
45
|
+
headers?: IncomingHttpHeaders | Record<string, string>;
|
|
46
|
+
/** HTTP method (GET, POST, PUT, DELETE, etc.) */
|
|
47
|
+
method?: HTTPMethods;
|
|
48
|
+
/** AbortSignal for cancelling the request */
|
|
49
|
+
signal?: AbortSignal;
|
|
50
|
+
/** Additional request configuration (implementation-specific) */
|
|
51
|
+
Request?: object;
|
|
52
|
+
/** Request payload/body data */
|
|
53
|
+
data?: TData;
|
|
54
|
+
/** URL query parameters as key-value pairs */
|
|
55
|
+
parameters?: TParams;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Standard HTTP response structure returned by all HTTP call strategies.
|
|
60
|
+
* @template T - The type of the parsed response data
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // For an API returning user data
|
|
64
|
+
* const response: IHttpCallStrategyResponse<{ id: number; name: string }> = await strategy.get('/users/1');
|
|
65
|
+
* console.log(response.data.name); // TypeScript knows data has 'name' property
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export interface IHttpCallStrategyResponse<T = any> {
|
|
69
|
+
/** The parsed response body */
|
|
70
|
+
data: T;
|
|
71
|
+
/** HTTP status code (e.g., 200, 404, 500) */
|
|
72
|
+
status: number;
|
|
73
|
+
/** HTTP status text (e.g., "OK", "Not Found") */
|
|
74
|
+
statusText: string;
|
|
75
|
+
/** Response headers from the server */
|
|
76
|
+
headers: OutgoingHttpHeaders;
|
|
77
|
+
/** Raw request object (implementation-specific) */
|
|
78
|
+
request?: any;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Interface defining the contract for HTTP-based API call strategies.
|
|
83
|
+
* Implementations must provide methods for making HTTP requests with type-safe request/response handling.
|
|
84
|
+
*/
|
|
85
|
+
export interface IHttpCallStrategy extends IApiCallStrategy {
|
|
86
|
+
/**
|
|
87
|
+
* Execute an HTTP request with full control over method, data, and parameters.
|
|
88
|
+
* @template TOptData - The type of request body data (default: string | object | Buffer | stream)
|
|
89
|
+
* @template TParams - The shape of URL query parameters (default: Record<string, any>)
|
|
90
|
+
* @template TResData - The expected type of the response body after parsing
|
|
91
|
+
* @param path - The URL path or full URL to request
|
|
92
|
+
* @param options - HTTP request configuration including method, headers, data, and parameters
|
|
93
|
+
* @returns Promise resolving to the HTTP response with typed data
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // POST request with typed request/response
|
|
97
|
+
* interface CreateUserDto { name: string; email: string; }
|
|
98
|
+
* interface UserResponse { id: number; name: string; email: string; }
|
|
99
|
+
*
|
|
100
|
+
* const response = await strategy.call<CreateUserDto, {}, UserResponse>('/users', {
|
|
101
|
+
* method: 'POST',
|
|
102
|
+
* data: { name: 'John', email: 'john@example.com' }
|
|
103
|
+
* });
|
|
104
|
+
* console.log(response.data.id); // TypeScript knows this is a number
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
call<TOptData, TParams extends Record<string, any>, TResData>(
|
|
108
|
+
path: string,
|
|
109
|
+
options?: HttpOptions<TOptData, TParams> | { method?: string },
|
|
110
|
+
): Promise<IHttpCallStrategyResponse<TResData>>;
|
|
111
|
+
/**
|
|
112
|
+
* Execute an HTTP GET request.
|
|
113
|
+
* @template TOptData - The type of request body data (typically unused for GET)
|
|
114
|
+
* @template TParams - The shape of URL query parameters
|
|
115
|
+
* @template TResData - The expected type of the response body
|
|
116
|
+
* @param path - The URL path or full URL to request
|
|
117
|
+
* @param options - HTTP request configuration (excluding method, which is fixed to GET)
|
|
118
|
+
* @returns Promise resolving to the HTTP response with typed data
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // GET request with query parameters
|
|
122
|
+
* interface UserListResponse { users: Array<{ id: number; name: string }>; total: number; }
|
|
123
|
+
*
|
|
124
|
+
* const response = await strategy.get<never, { page: string; limit: string }, UserListResponse>(
|
|
125
|
+
* '/users',
|
|
126
|
+
* { parameters: { page: '1', limit: '10' } }
|
|
127
|
+
* );
|
|
128
|
+
* console.log(response.data.total);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
get<TOptData, TParams extends Record<string, any>, TResData>(
|
|
132
|
+
path: string,
|
|
133
|
+
options?: HttpOptions<TOptData, TParams> | { method?: string },
|
|
134
|
+
): Promise<IHttpCallStrategyResponse<TResData>>;
|
|
135
|
+
/**
|
|
136
|
+
* Execute an HTTP POST request.
|
|
137
|
+
* @template TOptData - The type of request body data to send
|
|
138
|
+
* @template TParams - The shape of URL query parameters
|
|
139
|
+
* @template TResData - The expected type of the response body
|
|
140
|
+
* @param path - The URL path or full URL to request
|
|
141
|
+
* @param options - HTTP request configuration (excluding method, which is fixed to POST)
|
|
142
|
+
* @returns Promise resolving to the HTTP response with typed data
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* // POST request with body and query parameters
|
|
146
|
+
* interface CreateOrderDto { productId: number; quantity: number; }
|
|
147
|
+
* interface OrderResponse { orderId: string; total: number; }
|
|
148
|
+
*
|
|
149
|
+
* const response = await strategy.post<CreateOrderDto, { notify: string }, OrderResponse>(
|
|
150
|
+
* '/orders',
|
|
151
|
+
* {
|
|
152
|
+
* data: { productId: 42, quantity: 2 },
|
|
153
|
+
* parameters: { notify: 'true' }
|
|
154
|
+
* }
|
|
155
|
+
* );
|
|
156
|
+
* console.log(response.data.orderId);
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
post<TOptData, TParams extends Record<string, any>, TResData>(
|
|
160
|
+
path: string,
|
|
161
|
+
options?: HttpOptions<TOptData, TParams> | { method?: string },
|
|
162
|
+
): Promise<IHttpCallStrategyResponse<TResData>>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Abstract base class for HTTP-based API call strategies.
|
|
167
|
+
* Provides convenience methods (get, post) that delegate to the abstract `call` method.
|
|
168
|
+
* Concrete implementations must implement the `call` method for their specific transport mechanism
|
|
169
|
+
* (e.g., Axios, Fastify inject, native fetch).
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* class MyHttpStrategy extends HttpAbstractStrategy {
|
|
173
|
+
* async call<TOptData, TParams extends Record<string, any>, TResData>(
|
|
174
|
+
* path: string,
|
|
175
|
+
* options?: HttpOptions<TOptData, TParams>
|
|
176
|
+
* ): Promise<IHttpCallStrategyResponse<TResData>> {
|
|
177
|
+
* // Implementation using your HTTP client
|
|
178
|
+
* const response = await myHttpClient.request({ url: path, ...options });
|
|
179
|
+
* return { data: response.body, status: response.statusCode, ... };
|
|
180
|
+
* }
|
|
181
|
+
* }
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
export abstract class HttpAbstractStrategy implements IHttpCallStrategy {
|
|
185
|
+
/**
|
|
186
|
+
* Abstract method to execute an HTTP request. Must be implemented by concrete strategy classes.
|
|
187
|
+
* @template TOptData - The type of request body data being sent
|
|
188
|
+
* @template TParams - The shape of URL query parameters
|
|
189
|
+
* @template TResData - The expected type of the response body
|
|
190
|
+
* @param path - The URL path or full URL to request
|
|
191
|
+
* @param options - HTTP request configuration including method, headers, data, and parameters
|
|
192
|
+
* @returns Promise resolving to the HTTP response with typed data
|
|
193
|
+
*/
|
|
194
|
+
abstract call<TOptData, TParams extends Record<string, any>, TResData>(
|
|
195
|
+
path: string,
|
|
196
|
+
options?: HttpOptions<TOptData, TParams> | { method?: string },
|
|
197
|
+
): Promise<IHttpCallStrategyResponse<TResData>>;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Convenience method to execute an HTTP GET request.
|
|
201
|
+
* Delegates to `call()` with method set to 'GET'.
|
|
202
|
+
* @template TOptData - The type of request body data (typically unused for GET)
|
|
203
|
+
* @template TParams - The shape of URL query parameters
|
|
204
|
+
* @template TResData - The expected type of the response body
|
|
205
|
+
* @param path - The URL path or full URL to request
|
|
206
|
+
* @param options - HTTP request configuration (method will be overridden to GET)
|
|
207
|
+
* @returns Promise resolving to the HTTP response with typed data
|
|
208
|
+
*/
|
|
209
|
+
get<TOptData, TParams extends Record<string, any>, TResData>(
|
|
210
|
+
path: string,
|
|
211
|
+
options?: HttpOptions<TOptData, TParams> | { method?: string },
|
|
212
|
+
): Promise<IHttpCallStrategyResponse<TResData>> {
|
|
213
|
+
return this.call<TOptData, TParams, TResData>(path, {
|
|
214
|
+
...options,
|
|
215
|
+
method: 'GET',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Convenience method to execute an HTTP POST request.
|
|
221
|
+
* Delegates to `call()` with method set to 'POST'.
|
|
222
|
+
* @template TOptData - The type of request body data to send
|
|
223
|
+
* @template TParams - The shape of URL query parameters
|
|
224
|
+
* @template TResData - The expected type of the response body
|
|
225
|
+
* @param path - The URL path or full URL to request
|
|
226
|
+
* @param options - HTTP request configuration (method will be overridden to POST)
|
|
227
|
+
* @returns Promise resolving to the HTTP response with typed data
|
|
228
|
+
*/
|
|
229
|
+
post<TOptData, TParams extends Record<string, any>, TResData>(
|
|
230
|
+
path: string,
|
|
231
|
+
options?: HttpOptions<TOptData, TParams> | { method?: string },
|
|
232
|
+
): Promise<IHttpCallStrategyResponse<TResData>> {
|
|
233
|
+
return this.call<TOptData, TParams, TResData>(path, {
|
|
234
|
+
...options,
|
|
235
|
+
method: 'POST',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ClassType } from '@nest-yalc-2/types/globals.d.js';
|
|
2
|
+
import { HttpService } from '@nestjs/axios';
|
|
3
|
+
import { HttpAbstractStrategy, IHttpCallStrategyResponse, HttpOptions, IHttpCallStrategyOptions } from './http-abstract-call.strategy.js';
|
|
4
|
+
import { YalcGlobalClsService } from '@nest-yalc-2/app/cls.module.js';
|
|
5
|
+
export type NestHttpCallStrategyOptions = IHttpCallStrategyOptions & {
|
|
6
|
+
internalRequestHeader?: string;
|
|
7
|
+
internalRequestToken?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare class NestHttpCallStrategy extends HttpAbstractStrategy {
|
|
10
|
+
protected readonly httpService: HttpService;
|
|
11
|
+
protected readonly clsService: YalcGlobalClsService;
|
|
12
|
+
private baseUrl;
|
|
13
|
+
protected readonly options: NestHttpCallStrategyOptions;
|
|
14
|
+
private readonly internalHeader;
|
|
15
|
+
private readonly internalToken?;
|
|
16
|
+
constructor(httpService: HttpService, clsService: YalcGlobalClsService, baseUrl?: string, options?: NestHttpCallStrategyOptions);
|
|
17
|
+
call<TOptData, TParams extends Record<string, any>, TResData>(path: string, options?: HttpOptions<TOptData, TParams>): Promise<IHttpCallStrategyResponse<TResData>>;
|
|
18
|
+
}
|
|
19
|
+
export interface NestHttpCallStrategyProviderOptions {
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
NestHttpStrategy?: ClassType<NestHttpCallStrategy>;
|
|
22
|
+
headersWhitelist?: string[];
|
|
23
|
+
internalRequestHeader?: string;
|
|
24
|
+
internalRequestToken?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const NestHttpCallStrategyProvider: (provide: string, options?: NestHttpCallStrategyProviderOptions) => {
|
|
27
|
+
provide: string;
|
|
28
|
+
useFactory: (httpAdapter: HttpService, clsService: YalcGlobalClsService) => NestHttpCallStrategy;
|
|
29
|
+
inject: (typeof YalcGlobalClsService | typeof HttpService)[];
|
|
30
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NestHttpCallStrategyProvider = exports.NestHttpCallStrategy = void 0;
|
|
4
|
+
const axios_1 = require("@nestjs/axios");
|
|
5
|
+
const http_abstract_call_strategy_js_1 = require("./http-abstract-call.strategy.js");
|
|
6
|
+
const cls_module_js_1 = require("@nest-yalc-2/app/cls.module.js");
|
|
7
|
+
const header_whitelist_helper_js_1 = require("../header-whitelist.helper.js");
|
|
8
|
+
class NestHttpCallStrategy extends http_abstract_call_strategy_js_1.HttpAbstractStrategy {
|
|
9
|
+
constructor(httpService, clsService, baseUrl = '', options = {}) {
|
|
10
|
+
super();
|
|
11
|
+
this.httpService = httpService;
|
|
12
|
+
this.clsService = clsService;
|
|
13
|
+
this.baseUrl = baseUrl;
|
|
14
|
+
this.options = options;
|
|
15
|
+
this.internalHeader =
|
|
16
|
+
options.internalRequestHeader ?? 'x-internal-request-token';
|
|
17
|
+
this.internalToken = options.internalRequestToken;
|
|
18
|
+
}
|
|
19
|
+
async call(path, options) {
|
|
20
|
+
const clsHeaders = (0, header_whitelist_helper_js_1.filterHeaders)(this.clsService.get('headers'), this.options.headersWhitelist);
|
|
21
|
+
const headers = {
|
|
22
|
+
...clsHeaders,
|
|
23
|
+
...(options?.headers ?? {}),
|
|
24
|
+
};
|
|
25
|
+
if (this.internalHeader &&
|
|
26
|
+
this.internalToken &&
|
|
27
|
+
!headers[this.internalHeader]) {
|
|
28
|
+
headers[this.internalHeader] = this.internalToken;
|
|
29
|
+
}
|
|
30
|
+
const _options = {
|
|
31
|
+
headers,
|
|
32
|
+
method: options?.method,
|
|
33
|
+
signal: options?.signal,
|
|
34
|
+
data: options?.data,
|
|
35
|
+
};
|
|
36
|
+
if (options?.parameters) {
|
|
37
|
+
_options.params = new URLSearchParams(options.parameters);
|
|
38
|
+
}
|
|
39
|
+
const { data, ...res } = await this.httpService.axiosRef.request({
|
|
40
|
+
headers: _options?.headers,
|
|
41
|
+
method: _options?.method,
|
|
42
|
+
signal: _options?.signal,
|
|
43
|
+
data: _options?.data,
|
|
44
|
+
params: _options?.params,
|
|
45
|
+
url: `${this.baseUrl}${path}`,
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
...res,
|
|
49
|
+
headers: res.headers,
|
|
50
|
+
data,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.NestHttpCallStrategy = NestHttpCallStrategy;
|
|
55
|
+
const NestHttpCallStrategyProvider = (provide, options = {}) => ({
|
|
56
|
+
provide,
|
|
57
|
+
useFactory: (httpAdapter, clsService) => {
|
|
58
|
+
const _options = {
|
|
59
|
+
baseUrl: '',
|
|
60
|
+
NestHttpStrategy: NestHttpCallStrategy,
|
|
61
|
+
...options,
|
|
62
|
+
};
|
|
63
|
+
return new _options.NestHttpStrategy(httpAdapter, clsService, _options.baseUrl, _options);
|
|
64
|
+
},
|
|
65
|
+
inject: [axios_1.HttpService, cls_module_js_1.YalcGlobalClsService],
|
|
66
|
+
});
|
|
67
|
+
exports.NestHttpCallStrategyProvider = NestHttpCallStrategyProvider;
|
|
68
|
+
//# sourceMappingURL=nest-http-call.strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nest-http-call.strategy.js","sourceRoot":"","sources":["nest-http-call.strategy.ts"],"names":[],"mappings":";;;AACA,yCAA4C;AAC5C,qFAK0C;AAE1C,kEAAsE;AACtE,8EAA8D;AAQ9D,MAAa,oBAAqB,SAAQ,qDAAoB;IAI5D,YACqB,WAAwB,EACxB,UAAgC,EAC3C,UAAU,EAAE,EACD,UAAuC,EAAE;QAE5D,KAAK,EAAE,CAAC;QALW,gBAAW,GAAX,WAAW,CAAa;QACxB,eAAU,GAAV,UAAU,CAAsB;QAC3C,YAAO,GAAP,OAAO,CAAK;QACD,YAAO,GAAP,OAAO,CAAkC;QAG5D,IAAI,CAAC,cAAc;YACjB,OAAO,CAAC,qBAAqB,IAAI,0BAA0B,CAAC;QAC9D,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,oBAAoB,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,IAAI,CACR,IAAY,EACZ,OAAwC;QAExC,MAAM,UAAU,GAAG,IAAA,0CAAa,EAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAC9B,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAC9B,CAAC;QACF,MAAM,OAAO,GAAwB;YACnC,GAAG,UAAU;YACb,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;SAC5B,CAAC;QACF,IACE,IAAI,CAAC,cAAc;YACnB,IAAI,CAAC,aAAa;YAClB,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAC7B,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;QACpD,CAAC;QAKD,MAAM,QAAQ,GAIW;YACvB,OAAO;YACP,MAAM,EAAE,OAAO,EAAE,MAAM;YACvB,MAAM,EAAE,OAAO,EAAE,MAAM;YACvB,IAAI,EAAE,OAAO,EAAE,IAAI;SACpB,CAAC;QAEF,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;YACxB,QAAQ,CAAC,MAAM,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC/D,OAAO,EAAE,QAAQ,EAAE,OAAc;YACjC,MAAM,EAAE,QAAQ,EAAE,MAAM;YACxB,MAAM,EAAE,QAAQ,EAAE,MAAM;YACxB,IAAI,EAAE,QAAQ,EAAE,IAAI;YACpB,MAAM,EAAE,QAAQ,EAAE,MAAM;YACxB,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE;SAC9B,CAAC,CAAC;QAEH,OAAO;YACL,GAAG,GAAG;YACN,OAAO,EAAE,GAAG,CAAC,OAA8B;YAC3C,IAAI;SACL,CAAC;IACJ,CAAC;CACF;AArED,oDAqEC;AAaM,MAAM,4BAA4B,GAAG,CAC1C,OAAe,EACf,UAA+C,EAAE,EACjD,EAAE,CAAC,CAAC;IACJ,OAAO;IACP,UAAU,EAAE,CAAC,WAAwB,EAAE,UAAgC,EAAE,EAAE;QACzE,MAAM,QAAQ,GAAG;YACf,OAAO,EAAE,EAAE;YACX,gBAAgB,EAAE,oBAAoB;YACtC,GAAG,OAAO;SACX,CAAC;QAEF,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAClC,WAAW,EACX,UAAU,EACV,QAAQ,CAAC,OAAO,EAChB,QAAQ,CACT,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,CAAC,mBAAW,EAAE,oCAAoB,CAAC;CAC5C,CAAC,CAAC;AApBU,QAAA,4BAA4B,gCAoBtC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ClassType } from '@nest-yalc-2/types/globals.d.js';
|
|
2
|
+
import { HttpService } from '@nestjs/axios';
|
|
3
|
+
import {
|
|
4
|
+
HttpAbstractStrategy,
|
|
5
|
+
IHttpCallStrategyResponse,
|
|
6
|
+
HttpOptions,
|
|
7
|
+
IHttpCallStrategyOptions,
|
|
8
|
+
} from './http-abstract-call.strategy.js';
|
|
9
|
+
import { AxiosRequestConfig } from 'axios';
|
|
10
|
+
import { YalcGlobalClsService } from '@nest-yalc-2/app/cls.module.js';
|
|
11
|
+
import { filterHeaders } from '../header-whitelist.helper.js';
|
|
12
|
+
import type { OutgoingHttpHeaders } from 'node:http2';
|
|
13
|
+
|
|
14
|
+
export type NestHttpCallStrategyOptions = IHttpCallStrategyOptions & {
|
|
15
|
+
internalRequestHeader?: string;
|
|
16
|
+
internalRequestToken?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class NestHttpCallStrategy extends HttpAbstractStrategy {
|
|
20
|
+
private readonly internalHeader: string;
|
|
21
|
+
private readonly internalToken?: string;
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
protected readonly httpService: HttpService,
|
|
25
|
+
protected readonly clsService: YalcGlobalClsService,
|
|
26
|
+
private baseUrl = '',
|
|
27
|
+
protected readonly options: NestHttpCallStrategyOptions = {},
|
|
28
|
+
) {
|
|
29
|
+
super();
|
|
30
|
+
this.internalHeader =
|
|
31
|
+
options.internalRequestHeader ?? 'x-internal-request-token';
|
|
32
|
+
this.internalToken = options.internalRequestToken;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async call<TOptData, TParams extends Record<string, any>, TResData>(
|
|
36
|
+
path: string,
|
|
37
|
+
options?: HttpOptions<TOptData, TParams>,
|
|
38
|
+
): Promise<IHttpCallStrategyResponse<TResData>> {
|
|
39
|
+
const clsHeaders = filterHeaders(
|
|
40
|
+
this.clsService.get('headers'),
|
|
41
|
+
this.options.headersWhitelist,
|
|
42
|
+
);
|
|
43
|
+
const headers: Record<string, any> = {
|
|
44
|
+
...clsHeaders,
|
|
45
|
+
...(options?.headers ?? {}),
|
|
46
|
+
};
|
|
47
|
+
if (
|
|
48
|
+
this.internalHeader &&
|
|
49
|
+
this.internalToken &&
|
|
50
|
+
!headers[this.internalHeader]
|
|
51
|
+
) {
|
|
52
|
+
headers[this.internalHeader] = this.internalToken;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* We need this to do a type check on the options and
|
|
56
|
+
* implement the mapping from HttpOptions to AxiosRequestConfig
|
|
57
|
+
*/
|
|
58
|
+
const _options:
|
|
59
|
+
| {
|
|
60
|
+
[k: string | number | symbol]: never;
|
|
61
|
+
}
|
|
62
|
+
| AxiosRequestConfig = {
|
|
63
|
+
headers,
|
|
64
|
+
method: options?.method,
|
|
65
|
+
signal: options?.signal,
|
|
66
|
+
data: options?.data,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (options?.parameters) {
|
|
70
|
+
_options.params = new URLSearchParams(options.parameters);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { data, ...res } = await this.httpService.axiosRef.request({
|
|
74
|
+
headers: _options?.headers as any,
|
|
75
|
+
method: _options?.method,
|
|
76
|
+
signal: _options?.signal,
|
|
77
|
+
data: _options?.data,
|
|
78
|
+
params: _options?.params,
|
|
79
|
+
url: `${this.baseUrl}${path}`,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
...res,
|
|
84
|
+
headers: res.headers as OutgoingHttpHeaders,
|
|
85
|
+
data,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface NestHttpCallStrategyProviderOptions {
|
|
91
|
+
baseUrl?: string;
|
|
92
|
+
NestHttpStrategy?: ClassType<NestHttpCallStrategy>;
|
|
93
|
+
headersWhitelist?: string[];
|
|
94
|
+
internalRequestHeader?: string;
|
|
95
|
+
internalRequestToken?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Just a convenient provider to inject the NestLocalCallStrategy
|
|
100
|
+
*/
|
|
101
|
+
export const NestHttpCallStrategyProvider = (
|
|
102
|
+
provide: string,
|
|
103
|
+
options: NestHttpCallStrategyProviderOptions = {},
|
|
104
|
+
) => ({
|
|
105
|
+
provide,
|
|
106
|
+
useFactory: (httpAdapter: HttpService, clsService: YalcGlobalClsService) => {
|
|
107
|
+
const _options = {
|
|
108
|
+
baseUrl: '',
|
|
109
|
+
NestHttpStrategy: NestHttpCallStrategy,
|
|
110
|
+
...options,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return new _options.NestHttpStrategy(
|
|
114
|
+
httpAdapter,
|
|
115
|
+
clsService,
|
|
116
|
+
_options.baseUrl,
|
|
117
|
+
_options,
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
inject: [HttpService, YalcGlobalClsService],
|
|
121
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { HttpAdapterHost } from '@nestjs/core';
|
|
2
|
+
import { HttpAbstractStrategy, HttpOptions, IHttpCallStrategyOptions, IHttpCallStrategyResponse } from './http-abstract-call.strategy.js';
|
|
3
|
+
import { ClassType } from '@nest-yalc-2/types/globals.d.js';
|
|
4
|
+
import { YalcGlobalClsService } from '@nest-yalc-2/app/cls.module.js';
|
|
5
|
+
import { AppConfigService } from '@nest-yalc-2/app/app-config.service.js';
|
|
6
|
+
export type NestLocalCallStrategyOptions = IHttpCallStrategyOptions & {
|
|
7
|
+
internalRequestHeader?: string;
|
|
8
|
+
internalRequestToken?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class NestLocalCallStrategy extends HttpAbstractStrategy {
|
|
11
|
+
protected readonly adapterHost: HttpAdapterHost;
|
|
12
|
+
protected readonly clsService: YalcGlobalClsService;
|
|
13
|
+
protected readonly configService: AppConfigService;
|
|
14
|
+
private baseUrl;
|
|
15
|
+
protected readonly options: NestLocalCallStrategyOptions;
|
|
16
|
+
private readonly internalHeader;
|
|
17
|
+
private readonly internalToken?;
|
|
18
|
+
constructor(adapterHost: HttpAdapterHost, clsService: YalcGlobalClsService, configService: AppConfigService, baseUrl?: string, options?: NestLocalCallStrategyOptions);
|
|
19
|
+
call<TOptData extends string | object | Buffer | NodeJS.ReadableStream, TParams extends Record<string, any>, TResData>(path: string, options?: HttpOptions<TOptData, TParams>): Promise<IHttpCallStrategyResponse<TResData>>;
|
|
20
|
+
}
|
|
21
|
+
export interface NestLocalCallStrategyProviderOptions {
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
NestLocalStrategy?: ClassType<NestLocalCallStrategy>;
|
|
24
|
+
headersWhitelist?: string[];
|
|
25
|
+
internalRequestHeader?: string;
|
|
26
|
+
internalRequestToken?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const NestLocalCallStrategyProvider: (provide: string, options?: NestLocalCallStrategyProviderOptions) => {
|
|
29
|
+
provide: string;
|
|
30
|
+
useFactory: (httpAdapter: HttpAdapterHost, clsService: YalcGlobalClsService, configService: AppConfigService) => NestLocalCallStrategy;
|
|
31
|
+
inject: (string | typeof YalcGlobalClsService | typeof HttpAdapterHost)[];
|
|
32
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NestLocalCallStrategyProvider = exports.NestLocalCallStrategy = void 0;
|
|
4
|
+
const core_1 = require("@nestjs/core");
|
|
5
|
+
const http_abstract_call_strategy_js_1 = require("./http-abstract-call.strategy.js");
|
|
6
|
+
const cls_module_js_1 = require("@nest-yalc-2/app/cls.module.js");
|
|
7
|
+
const header_whitelist_helper_js_1 = require("../header-whitelist.helper.js");
|
|
8
|
+
const def_const_js_1 = require("@nest-yalc-2/app/def.const.js");
|
|
9
|
+
class NestLocalCallStrategy extends http_abstract_call_strategy_js_1.HttpAbstractStrategy {
|
|
10
|
+
constructor(adapterHost, clsService, configService, baseUrl = '', options = {}) {
|
|
11
|
+
super();
|
|
12
|
+
this.adapterHost = adapterHost;
|
|
13
|
+
this.clsService = clsService;
|
|
14
|
+
this.configService = configService;
|
|
15
|
+
this.baseUrl = baseUrl;
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.internalHeader =
|
|
18
|
+
options.internalRequestHeader ?? 'x-internal-request-token';
|
|
19
|
+
this.internalToken =
|
|
20
|
+
options.internalRequestToken ??
|
|
21
|
+
configService?.values?.internalRequestToken;
|
|
22
|
+
}
|
|
23
|
+
async call(path, options) {
|
|
24
|
+
const instance = this.adapterHost.httpAdapter.getInstance();
|
|
25
|
+
const clsHeaders = (0, header_whitelist_helper_js_1.filterHeaders)(this.clsService.get('headers'), this.options.headersWhitelist);
|
|
26
|
+
const headers = {
|
|
27
|
+
...clsHeaders,
|
|
28
|
+
...(options?.headers ?? {}),
|
|
29
|
+
};
|
|
30
|
+
if (this.internalHeader &&
|
|
31
|
+
this.internalToken &&
|
|
32
|
+
!headers[this.internalHeader]) {
|
|
33
|
+
headers[this.internalHeader] = this.internalToken;
|
|
34
|
+
}
|
|
35
|
+
const _options = {
|
|
36
|
+
headers,
|
|
37
|
+
method: options?.method,
|
|
38
|
+
payload: options?.data,
|
|
39
|
+
};
|
|
40
|
+
const args = {
|
|
41
|
+
..._options,
|
|
42
|
+
url: `${this.baseUrl}${path}`,
|
|
43
|
+
};
|
|
44
|
+
if (options?.parameters) {
|
|
45
|
+
args.query = Object.fromEntries(new URLSearchParams(options.parameters));
|
|
46
|
+
}
|
|
47
|
+
const result = await instance.inject(args);
|
|
48
|
+
let data;
|
|
49
|
+
try {
|
|
50
|
+
if (this.options.shouldSkipJsonParse &&
|
|
51
|
+
this.options.shouldSkipJsonParse(result.body)) {
|
|
52
|
+
data = result.body;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
data = result.json();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (_e) {
|
|
59
|
+
data = result.body;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
data,
|
|
63
|
+
headers: result.headers,
|
|
64
|
+
status: result.statusCode,
|
|
65
|
+
statusText: result.statusMessage,
|
|
66
|
+
request: result.payload,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.NestLocalCallStrategy = NestLocalCallStrategy;
|
|
71
|
+
const NestLocalCallStrategyProvider = (provide, options = {}) => ({
|
|
72
|
+
provide,
|
|
73
|
+
useFactory: (httpAdapter, clsService, configService) => {
|
|
74
|
+
const _options = {
|
|
75
|
+
baseUrl: '',
|
|
76
|
+
NestLocalStrategy: NestLocalCallStrategy,
|
|
77
|
+
...options,
|
|
78
|
+
};
|
|
79
|
+
return new _options.NestLocalStrategy(httpAdapter, clsService, configService, _options.baseUrl, _options);
|
|
80
|
+
},
|
|
81
|
+
inject: [core_1.HttpAdapterHost, cls_module_js_1.YalcGlobalClsService, def_const_js_1.MAIN_APP_CONFIG_SERVICE],
|
|
82
|
+
});
|
|
83
|
+
exports.NestLocalCallStrategyProvider = NestLocalCallStrategyProvider;
|
|
84
|
+
//# sourceMappingURL=nest-local-call.strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nest-local-call.strategy.js","sourceRoot":"","sources":["nest-local-call.strategy.ts"],"names":[],"mappings":"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"}
|