@nest-yalc-2/api-strategy 2.2.1 → 2.2.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.
Files changed (30) hide show
  1. package/package.json +10 -2
  2. package/src/__tests__/call-strategy-wrappers.spec.ts +217 -217
  3. package/src/__tests__/context-call.service.spec.ts +52 -52
  4. package/src/__tests__/context-event.service.spec.ts +52 -52
  5. package/src/__tests__/event-strategy-composition.spec.ts +185 -185
  6. package/src/__tests__/header-whitelist.helper.spec.ts +41 -30
  7. package/src/__tests__/http-abstract-call.strategy.spec.ts +17 -17
  8. package/src/__tests__/nest-http-call.strategy.spec.ts +164 -164
  9. package/src/__tests__/nest-local-call.strategy.spec.ts +263 -263
  10. package/src/__tests__/nest-local-event.strategy.spec.ts +55 -55
  11. package/src/__tests__/rabbitmq-event.strategy.spec.ts +220 -220
  12. package/src/__tests__/strategy-selector.provider.spec.ts +182 -182
  13. package/src/context-call.interface.ts +49 -49
  14. package/src/context-call.service.ts +31 -31
  15. package/src/context-event.interface.ts +4 -4
  16. package/src/context-event.service.ts +31 -31
  17. package/src/header-whitelist.helper.ts +12 -12
  18. package/src/index.ts +17 -17
  19. package/src/strategies/composite-event.strategy.ts +76 -76
  20. package/src/strategies/conditional-call.strategy.ts +70 -70
  21. package/src/strategies/conditional-event.strategy.ts +54 -54
  22. package/src/strategies/fallback-call.strategy.ts +67 -67
  23. package/src/strategies/http-abstract-call.strategy.ts +238 -238
  24. package/src/strategies/nest-http-call.strategy.ts +121 -121
  25. package/src/strategies/nest-local-call.strategy.ts +153 -153
  26. package/src/strategies/nest-local-event.strategy.d.ts +1 -1
  27. package/src/strategies/nest-local-event.strategy.ts +42 -42
  28. package/src/strategies/rabbitmq-event.strategy.ts +154 -154
  29. package/src/strategies/shadow-call.strategy.ts +87 -87
  30. package/src/strategy-selector.provider.ts +103 -103
@@ -1,76 +1,76 @@
1
- import { OnModuleDestroy } from '@nestjs/common';
2
- import type { IEventStrategy } from '../context-event.interface.js';
3
-
4
- export type CompositeEventStrategyErrorMode = 'throw' | 'ignore';
5
-
6
- export interface CompositeEventStrategyOptions {
7
- errorMode?: CompositeEventStrategyErrorMode;
8
- }
9
-
10
- export class CompositeEventStrategy<P = any, R = any, O = any>
11
- implements IEventStrategy<P, R[], O>, OnModuleDestroy
12
- {
13
- private readonly errorMode: CompositeEventStrategyErrorMode;
14
-
15
- constructor(
16
- private readonly strategies: IEventStrategy<P, R, O>[],
17
- options: CompositeEventStrategyOptions = {},
18
- ) {
19
- if (strategies.length === 0) {
20
- throw new Error('CompositeEventStrategy requires at least one strategy.');
21
- }
22
-
23
- this.errorMode = options.errorMode ?? 'throw';
24
- }
25
-
26
- emit(path: string, payload: P, options?: O): R[] {
27
- const results: R[] = [];
28
-
29
- for (const strategy of this.strategies) {
30
- try {
31
- results.push(strategy.emit(path, payload, options));
32
- } catch (error) {
33
- if (this.errorMode === 'throw') {
34
- throw error;
35
- }
36
- }
37
- }
38
-
39
- return results;
40
- }
41
-
42
- async emitAsync(path: string, payload: P, options?: O): Promise<R[]> {
43
- const settled = await Promise.allSettled(
44
- this.strategies.map((strategy) =>
45
- strategy.emitAsync(path, payload, options),
46
- ),
47
- );
48
- const results: R[] = [];
49
-
50
- for (const result of settled) {
51
- if (result.status === 'fulfilled') {
52
- results.push(result.value);
53
- } else if (this.errorMode === 'throw') {
54
- throw result.reason;
55
- }
56
- }
57
-
58
- return results;
59
- }
60
-
61
- async onModuleDestroy() {
62
- await Promise.all(
63
- this.strategies.map(async (strategy) => {
64
- await maybeDestroyStrategy(strategy);
65
- }),
66
- );
67
- }
68
- }
69
-
70
- async function maybeDestroyStrategy(strategy: unknown) {
71
- const destroyable = strategy as Partial<OnModuleDestroy>;
72
-
73
- if (typeof destroyable.onModuleDestroy === 'function') {
74
- await destroyable.onModuleDestroy();
75
- }
76
- }
1
+ import { OnModuleDestroy } from '@nestjs/common';
2
+ import type { IEventStrategy } from '../context-event.interface.js';
3
+
4
+ export type CompositeEventStrategyErrorMode = 'throw' | 'ignore';
5
+
6
+ export interface CompositeEventStrategyOptions {
7
+ errorMode?: CompositeEventStrategyErrorMode;
8
+ }
9
+
10
+ export class CompositeEventStrategy<P = any, R = any, O = any>
11
+ implements IEventStrategy<P, R[], O>, OnModuleDestroy
12
+ {
13
+ private readonly errorMode: CompositeEventStrategyErrorMode;
14
+
15
+ constructor(
16
+ private readonly strategies: IEventStrategy<P, R, O>[],
17
+ options: CompositeEventStrategyOptions = {},
18
+ ) {
19
+ if (strategies.length === 0) {
20
+ throw new Error('CompositeEventStrategy requires at least one strategy.');
21
+ }
22
+
23
+ this.errorMode = options.errorMode ?? 'throw';
24
+ }
25
+
26
+ emit(path: string, payload: P, options?: O): R[] {
27
+ const results: R[] = [];
28
+
29
+ for (const strategy of this.strategies) {
30
+ try {
31
+ results.push(strategy.emit(path, payload, options));
32
+ } catch (error) {
33
+ if (this.errorMode === 'throw') {
34
+ throw error;
35
+ }
36
+ }
37
+ }
38
+
39
+ return results;
40
+ }
41
+
42
+ async emitAsync(path: string, payload: P, options?: O): Promise<R[]> {
43
+ const settled = await Promise.allSettled(
44
+ this.strategies.map((strategy) =>
45
+ strategy.emitAsync(path, payload, options),
46
+ ),
47
+ );
48
+ const results: R[] = [];
49
+
50
+ for (const result of settled) {
51
+ if (result.status === 'fulfilled') {
52
+ results.push(result.value);
53
+ } else if (this.errorMode === 'throw') {
54
+ throw result.reason;
55
+ }
56
+ }
57
+
58
+ return results;
59
+ }
60
+
61
+ async onModuleDestroy() {
62
+ await Promise.all(
63
+ this.strategies.map(async (strategy) => {
64
+ await maybeDestroyStrategy(strategy);
65
+ }),
66
+ );
67
+ }
68
+ }
69
+
70
+ async function maybeDestroyStrategy(strategy: unknown) {
71
+ const destroyable = strategy as Partial<OnModuleDestroy>;
72
+
73
+ if (typeof destroyable.onModuleDestroy === 'function') {
74
+ await destroyable.onModuleDestroy();
75
+ }
76
+ }
@@ -1,70 +1,70 @@
1
- import {
2
- IApiCallStrategy,
3
- ICallOptions,
4
- IObjectWithData,
5
- } from '../context-call.interface.js';
6
-
7
- export interface ConditionalCallStrategyOptions {
8
- enabled?: boolean | (() => boolean);
9
- disabledResponse?: IObjectWithData<any>;
10
- disabledError?: Error | (() => Error);
11
- }
12
-
13
- export class ConditionalCallStrategy implements IApiCallStrategy {
14
- constructor(
15
- private readonly strategy: IApiCallStrategy,
16
- private readonly options: ConditionalCallStrategyOptions = {},
17
- ) {}
18
-
19
- call<TOptData, TParams extends Record<string, any>, TResData>(
20
- path: string,
21
- options?: ICallOptions<TOptData, TParams>,
22
- ): Promise<IObjectWithData<TResData>> {
23
- return this.whenEnabled(() => this.strategy.call(path, options));
24
- }
25
-
26
- get<TOptData, TParams extends Record<string, any>, TResData>(
27
- path: string,
28
- options?: ICallOptions<TOptData, TParams>,
29
- ): Promise<IObjectWithData<TResData>> {
30
- return this.whenEnabled(() => this.strategy.get(path, options));
31
- }
32
-
33
- post<TOptData, TParams extends Record<string, any>, TResData>(
34
- path: string,
35
- options?: ICallOptions<TOptData, TParams>,
36
- ): Promise<IObjectWithData<TResData>> {
37
- return this.whenEnabled(() => this.strategy.post(path, options));
38
- }
39
-
40
- private async whenEnabled<TResData>(
41
- execute: () => Promise<IObjectWithData<TResData>>,
42
- ) {
43
- if (this.isEnabled()) {
44
- return execute();
45
- }
46
-
47
- if (this.options.disabledResponse) {
48
- return this.options.disabledResponse as IObjectWithData<TResData>;
49
- }
50
-
51
- throw this.resolveDisabledError();
52
- }
53
-
54
- private isEnabled() {
55
- return typeof this.options.enabled === 'function'
56
- ? this.options.enabled()
57
- : this.options.enabled !== false;
58
- }
59
-
60
- private resolveDisabledError() {
61
- if (typeof this.options.disabledError === 'function') {
62
- return this.options.disabledError();
63
- }
64
-
65
- return (
66
- this.options.disabledError ??
67
- new Error('ConditionalCallStrategy is disabled.')
68
- );
69
- }
70
- }
1
+ import {
2
+ IApiCallStrategy,
3
+ ICallOptions,
4
+ IObjectWithData,
5
+ } from '../context-call.interface.js';
6
+
7
+ export interface ConditionalCallStrategyOptions {
8
+ enabled?: boolean | (() => boolean);
9
+ disabledResponse?: IObjectWithData<any>;
10
+ disabledError?: Error | (() => Error);
11
+ }
12
+
13
+ export class ConditionalCallStrategy implements IApiCallStrategy {
14
+ constructor(
15
+ private readonly strategy: IApiCallStrategy,
16
+ private readonly options: ConditionalCallStrategyOptions = {},
17
+ ) {}
18
+
19
+ call<TOptData, TParams extends Record<string, any>, TResData>(
20
+ path: string,
21
+ options?: ICallOptions<TOptData, TParams>,
22
+ ): Promise<IObjectWithData<TResData>> {
23
+ return this.whenEnabled(() => this.strategy.call(path, options));
24
+ }
25
+
26
+ get<TOptData, TParams extends Record<string, any>, TResData>(
27
+ path: string,
28
+ options?: ICallOptions<TOptData, TParams>,
29
+ ): Promise<IObjectWithData<TResData>> {
30
+ return this.whenEnabled(() => this.strategy.get(path, options));
31
+ }
32
+
33
+ post<TOptData, TParams extends Record<string, any>, TResData>(
34
+ path: string,
35
+ options?: ICallOptions<TOptData, TParams>,
36
+ ): Promise<IObjectWithData<TResData>> {
37
+ return this.whenEnabled(() => this.strategy.post(path, options));
38
+ }
39
+
40
+ private async whenEnabled<TResData>(
41
+ execute: () => Promise<IObjectWithData<TResData>>,
42
+ ) {
43
+ if (this.isEnabled()) {
44
+ return execute();
45
+ }
46
+
47
+ if (this.options.disabledResponse) {
48
+ return this.options.disabledResponse as IObjectWithData<TResData>;
49
+ }
50
+
51
+ throw this.resolveDisabledError();
52
+ }
53
+
54
+ private isEnabled() {
55
+ return typeof this.options.enabled === 'function'
56
+ ? this.options.enabled()
57
+ : this.options.enabled !== false;
58
+ }
59
+
60
+ private resolveDisabledError() {
61
+ if (typeof this.options.disabledError === 'function') {
62
+ return this.options.disabledError();
63
+ }
64
+
65
+ return (
66
+ this.options.disabledError ??
67
+ new Error('ConditionalCallStrategy is disabled.')
68
+ );
69
+ }
70
+ }
@@ -1,54 +1,54 @@
1
- import { OnModuleDestroy } from '@nestjs/common';
2
- import type { IEventStrategy } from '../context-event.interface.js';
3
-
4
- export interface ConditionalEventStrategyOptions<P = any, R = any, O = any> {
5
- enabled?: boolean | (() => boolean);
6
- shouldEmit?: (path: string, payload: P, options?: O) => boolean;
7
- disabledResult?: R;
8
- }
9
-
10
- export class ConditionalEventStrategy<P = any, R = any, O = any>
11
- implements IEventStrategy<P, R, O>, OnModuleDestroy
12
- {
13
- constructor(
14
- private readonly strategy: IEventStrategy<P, R, O>,
15
- private readonly options: ConditionalEventStrategyOptions<P, R, O> = {},
16
- ) {}
17
-
18
- emit(path: string, payload: P, options?: O): R {
19
- if (!this.shouldRun(path, payload, options)) {
20
- return this.options.disabledResult as R;
21
- }
22
-
23
- return this.strategy.emit(path, payload, options);
24
- }
25
-
26
- async emitAsync(path: string, payload: P, options?: O): Promise<R> {
27
- if (!this.shouldRun(path, payload, options)) {
28
- return this.options.disabledResult as R;
29
- }
30
-
31
- return this.strategy.emitAsync(path, payload, options);
32
- }
33
-
34
- async onModuleDestroy() {
35
- const destroyable = this.strategy as Partial<OnModuleDestroy>;
36
-
37
- if (typeof destroyable.onModuleDestroy === 'function') {
38
- await destroyable.onModuleDestroy();
39
- }
40
- }
41
-
42
- private shouldRun(path: string, payload: P, options?: O) {
43
- const enabled =
44
- typeof this.options.enabled === 'function'
45
- ? this.options.enabled()
46
- : this.options.enabled;
47
-
48
- if (enabled === false) {
49
- return false;
50
- }
51
-
52
- return this.options.shouldEmit?.(path, payload, options) ?? true;
53
- }
54
- }
1
+ import { OnModuleDestroy } from '@nestjs/common';
2
+ import type { IEventStrategy } from '../context-event.interface.js';
3
+
4
+ export interface ConditionalEventStrategyOptions<P = any, R = any, O = any> {
5
+ enabled?: boolean | (() => boolean);
6
+ shouldEmit?: (path: string, payload: P, options?: O) => boolean;
7
+ disabledResult?: R;
8
+ }
9
+
10
+ export class ConditionalEventStrategy<P = any, R = any, O = any>
11
+ implements IEventStrategy<P, R, O>, OnModuleDestroy
12
+ {
13
+ constructor(
14
+ private readonly strategy: IEventStrategy<P, R, O>,
15
+ private readonly options: ConditionalEventStrategyOptions<P, R, O> = {},
16
+ ) {}
17
+
18
+ emit(path: string, payload: P, options?: O): R {
19
+ if (!this.shouldRun(path, payload, options)) {
20
+ return this.options.disabledResult as R;
21
+ }
22
+
23
+ return this.strategy.emit(path, payload, options);
24
+ }
25
+
26
+ async emitAsync(path: string, payload: P, options?: O): Promise<R> {
27
+ if (!this.shouldRun(path, payload, options)) {
28
+ return this.options.disabledResult as R;
29
+ }
30
+
31
+ return this.strategy.emitAsync(path, payload, options);
32
+ }
33
+
34
+ async onModuleDestroy() {
35
+ const destroyable = this.strategy as Partial<OnModuleDestroy>;
36
+
37
+ if (typeof destroyable.onModuleDestroy === 'function') {
38
+ await destroyable.onModuleDestroy();
39
+ }
40
+ }
41
+
42
+ private shouldRun(path: string, payload: P, options?: O) {
43
+ const enabled =
44
+ typeof this.options.enabled === 'function'
45
+ ? this.options.enabled()
46
+ : this.options.enabled;
47
+
48
+ if (enabled === false) {
49
+ return false;
50
+ }
51
+
52
+ return this.options.shouldEmit?.(path, payload, options) ?? true;
53
+ }
54
+ }
@@ -1,67 +1,67 @@
1
- import {
2
- IApiCallStrategy,
3
- ICallOptions,
4
- IObjectWithData,
5
- } from '../context-call.interface.js';
6
-
7
- export interface FallbackCallStrategyOptions {
8
- shouldFallback?: (error: unknown, strategyIndex: number) => boolean;
9
- }
10
-
11
- export class FallbackCallStrategy implements IApiCallStrategy {
12
- constructor(
13
- private readonly strategies: IApiCallStrategy[],
14
- private readonly options: FallbackCallStrategyOptions = {},
15
- ) {
16
- if (strategies.length === 0) {
17
- throw new Error('FallbackCallStrategy requires at least one strategy.');
18
- }
19
- }
20
-
21
- call<TOptData, TParams extends Record<string, any>, TResData>(
22
- path: string,
23
- options?: ICallOptions<TOptData, TParams>,
24
- ): Promise<IObjectWithData<TResData>> {
25
- return this.execute((strategy) => strategy.call(path, options));
26
- }
27
-
28
- get<TOptData, TParams extends Record<string, any>, TResData>(
29
- path: string,
30
- options?: ICallOptions<TOptData, TParams>,
31
- ): Promise<IObjectWithData<TResData>> {
32
- return this.execute((strategy) => strategy.get(path, options));
33
- }
34
-
35
- post<TOptData, TParams extends Record<string, any>, TResData>(
36
- path: string,
37
- options?: ICallOptions<TOptData, TParams>,
38
- ): Promise<IObjectWithData<TResData>> {
39
- return this.execute((strategy) => strategy.post(path, options));
40
- }
41
-
42
- private async execute<TResData>(
43
- callStrategy: (
44
- strategy: IApiCallStrategy,
45
- ) => Promise<IObjectWithData<TResData>>,
46
- ) {
47
- let lastError: unknown;
48
-
49
- for (const [index, strategy] of this.strategies.entries()) {
50
- try {
51
- return await callStrategy(strategy);
52
- } catch (error) {
53
- lastError = error;
54
-
55
- if (!this.shouldFallback(error, index)) {
56
- throw error;
57
- }
58
- }
59
- }
60
-
61
- throw lastError;
62
- }
63
-
64
- private shouldFallback(error: unknown, strategyIndex: number) {
65
- return this.options.shouldFallback?.(error, strategyIndex) ?? true;
66
- }
67
- }
1
+ import {
2
+ IApiCallStrategy,
3
+ ICallOptions,
4
+ IObjectWithData,
5
+ } from '../context-call.interface.js';
6
+
7
+ export interface FallbackCallStrategyOptions {
8
+ shouldFallback?: (error: unknown, strategyIndex: number) => boolean;
9
+ }
10
+
11
+ export class FallbackCallStrategy implements IApiCallStrategy {
12
+ constructor(
13
+ private readonly strategies: IApiCallStrategy[],
14
+ private readonly options: FallbackCallStrategyOptions = {},
15
+ ) {
16
+ if (strategies.length === 0) {
17
+ throw new Error('FallbackCallStrategy requires at least one strategy.');
18
+ }
19
+ }
20
+
21
+ call<TOptData, TParams extends Record<string, any>, TResData>(
22
+ path: string,
23
+ options?: ICallOptions<TOptData, TParams>,
24
+ ): Promise<IObjectWithData<TResData>> {
25
+ return this.execute((strategy) => strategy.call(path, options));
26
+ }
27
+
28
+ get<TOptData, TParams extends Record<string, any>, TResData>(
29
+ path: string,
30
+ options?: ICallOptions<TOptData, TParams>,
31
+ ): Promise<IObjectWithData<TResData>> {
32
+ return this.execute((strategy) => strategy.get(path, options));
33
+ }
34
+
35
+ post<TOptData, TParams extends Record<string, any>, TResData>(
36
+ path: string,
37
+ options?: ICallOptions<TOptData, TParams>,
38
+ ): Promise<IObjectWithData<TResData>> {
39
+ return this.execute((strategy) => strategy.post(path, options));
40
+ }
41
+
42
+ private async execute<TResData>(
43
+ callStrategy: (
44
+ strategy: IApiCallStrategy,
45
+ ) => Promise<IObjectWithData<TResData>>,
46
+ ) {
47
+ let lastError: unknown;
48
+
49
+ for (const [index, strategy] of this.strategies.entries()) {
50
+ try {
51
+ return await callStrategy(strategy);
52
+ } catch (error) {
53
+ lastError = error;
54
+
55
+ if (!this.shouldFallback(error, index)) {
56
+ throw error;
57
+ }
58
+ }
59
+ }
60
+
61
+ throw lastError;
62
+ }
63
+
64
+ private shouldFallback(error: unknown, strategyIndex: number) {
65
+ return this.options.shouldFallback?.(error, strategyIndex) ?? true;
66
+ }
67
+ }