@node-c/core 1.0.0-alpha8 → 1.0.0-beta0

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 (140) hide show
  1. package/coverage/src/app.ts.html +349 -0
  2. package/coverage/src/common/configProvider/configProvider.module.ts.html +160 -0
  3. package/coverage/src/common/configProvider/configProvider.service.ts.html +658 -0
  4. package/coverage/src/common/configProvider/index.html +146 -0
  5. package/coverage/src/common/configProvider/index.ts.html +94 -0
  6. package/coverage/src/common/definitions/common.constants.ts.html +160 -0
  7. package/coverage/src/common/definitions/common.errors.ts.html +124 -0
  8. package/coverage/src/common/definitions/index.html +146 -0
  9. package/coverage/src/common/definitions/index.ts.html +94 -0
  10. package/coverage/src/common/utils/index.html +131 -0
  11. package/coverage/src/common/utils/index.ts.html +88 -0
  12. package/coverage/src/common/utils/utils.loadDynamicModules.ts.html +265 -0
  13. package/coverage/src/domain/entityService/domain.entity.service.ts.html +784 -0
  14. package/coverage/src/domain/entityService/index.html +131 -0
  15. package/coverage/src/domain/entityService/index.ts.html +91 -0
  16. package/coverage/src/index.html +116 -0
  17. package/coverage/src/persistance/entityService/index.html +131 -0
  18. package/coverage/src/persistance/entityService/index.ts.html +91 -0
  19. package/coverage/src/persistance/entityService/persistance.entity.service.ts.html +268 -0
  20. package/dist/app.d.ts +2 -2
  21. package/dist/app.js +27 -15
  22. package/dist/app.js.map +1 -1
  23. package/dist/common/configProvider/configProvider.definitions.d.ts +156 -20
  24. package/dist/common/configProvider/configProvider.definitions.js +21 -7
  25. package/dist/common/configProvider/configProvider.definitions.js.map +1 -1
  26. package/dist/common/configProvider/configProvider.module.js +13 -2
  27. package/dist/common/configProvider/configProvider.module.js.map +1 -1
  28. package/dist/common/configProvider/configProvider.service.js +21 -18
  29. package/dist/common/configProvider/configProvider.service.js.map +1 -1
  30. package/dist/common/definitions/common.constants.d.ts +2 -1
  31. package/dist/common/definitions/common.constants.js +1 -0
  32. package/dist/common/definitions/common.constants.js.map +1 -1
  33. package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.d.ts +1 -0
  34. package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.js +9 -0
  35. package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.js.map +1 -0
  36. package/dist/common/utils/base64UrlEncode/index.d.ts +1 -0
  37. package/dist/{persistance/common/entityService → common/utils/base64UrlEncode}/index.js +1 -2
  38. package/dist/common/utils/base64UrlEncode/index.js.map +1 -0
  39. package/dist/common/utils/getNested/getNested.definitions.d.ts +4 -0
  40. package/dist/common/utils/getNested/getNested.definitions.js +3 -0
  41. package/dist/common/utils/getNested/getNested.definitions.js.map +1 -0
  42. package/dist/common/utils/getNested/getNested.method.d.ts +6 -0
  43. package/dist/common/utils/getNested/getNested.method.js +88 -0
  44. package/dist/common/utils/getNested/getNested.method.js.map +1 -0
  45. package/dist/common/utils/getNested/index.d.ts +2 -0
  46. package/dist/common/utils/getNested/index.js +19 -0
  47. package/dist/common/utils/getNested/index.js.map +1 -0
  48. package/dist/common/utils/httpRequest/httpRequest.definitions.d.ts +19 -0
  49. package/dist/common/utils/httpRequest/httpRequest.definitions.js +3 -0
  50. package/dist/common/utils/httpRequest/httpRequest.definitions.js.map +1 -0
  51. package/dist/common/utils/httpRequest/httpRequest.method.d.ts +2 -0
  52. package/dist/common/utils/httpRequest/httpRequest.method.js +56 -0
  53. package/dist/common/utils/httpRequest/httpRequest.method.js.map +1 -0
  54. package/dist/common/utils/httpRequest/index.d.ts +2 -0
  55. package/dist/common/utils/httpRequest/index.js +19 -0
  56. package/dist/common/utils/httpRequest/index.js.map +1 -0
  57. package/dist/common/utils/index.d.ts +5 -1
  58. package/dist/common/utils/index.js +5 -1
  59. package/dist/common/utils/index.js.map +1 -1
  60. package/dist/common/utils/loadDynamicModules/index.d.ts +1 -0
  61. package/dist/common/utils/loadDynamicModules/index.js +18 -0
  62. package/dist/common/utils/loadDynamicModules/index.js.map +1 -0
  63. package/dist/common/utils/{utils.loadDynamicModules.d.ts → loadDynamicModules/utils.loadDynamicModules.d.ts} +1 -1
  64. package/dist/common/utils/loadDynamicModules/utils.loadDynamicModules.js.map +1 -0
  65. package/dist/common/utils/setNested/index.d.ts +2 -0
  66. package/dist/common/utils/setNested/index.js +19 -0
  67. package/dist/common/utils/setNested/index.js.map +1 -0
  68. package/dist/common/utils/setNested/setNested.definitions.d.ts +4 -0
  69. package/dist/common/utils/setNested/setNested.definitions.js +3 -0
  70. package/dist/common/utils/setNested/setNested.definitions.js.map +1 -0
  71. package/dist/common/utils/setNested/setNested.method.d.ts +2 -0
  72. package/dist/common/utils/setNested/setNested.method.js +70 -0
  73. package/dist/common/utils/setNested/setNested.method.js.map +1 -0
  74. package/dist/data/entityService/data.entity.service.d.ts +17 -0
  75. package/dist/data/entityService/data.entity.service.definitions.d.ts +117 -0
  76. package/dist/data/entityService/data.entity.service.definitions.js +28 -0
  77. package/dist/data/entityService/data.entity.service.definitions.js.map +1 -0
  78. package/dist/{persistance/common/entityService/persistance.entity.service.js → data/entityService/data.entity.service.js} +42 -10
  79. package/dist/data/entityService/data.entity.service.js.map +1 -0
  80. package/dist/data/entityService/index.d.ts +2 -0
  81. package/dist/data/entityService/index.js +19 -0
  82. package/dist/data/entityService/index.js.map +1 -0
  83. package/dist/domain/entityService/domain.entity.service.d.ts +28 -13
  84. package/dist/domain/entityService/domain.entity.service.definitions.d.ts +26 -17
  85. package/dist/domain/entityService/domain.entity.service.definitions.js +6 -6
  86. package/dist/domain/entityService/domain.entity.service.definitions.js.map +1 -1
  87. package/dist/domain/entityService/domain.entity.service.js +97 -56
  88. package/dist/domain/entityService/domain.entity.service.js.map +1 -1
  89. package/dist/index.d.ts +1 -1
  90. package/dist/index.js +1 -1
  91. package/dist/index.js.map +1 -1
  92. package/package.json +11 -10
  93. package/src/app.spec.ts +138 -0
  94. package/src/app.ts +115 -0
  95. package/src/common/configProvider/configProvider.definitions.ts +445 -0
  96. package/src/common/configProvider/configProvider.module.spec.ts +90 -0
  97. package/src/common/configProvider/configProvider.module.ts +25 -0
  98. package/src/common/configProvider/configProvider.service.spec.ts +206 -0
  99. package/src/common/configProvider/configProvider.service.ts +204 -0
  100. package/src/common/configProvider/index.ts +3 -0
  101. package/src/common/definitions/common.constants.ts +27 -0
  102. package/src/common/definitions/common.definitions.ts +13 -0
  103. package/src/common/definitions/common.errors.ts +13 -0
  104. package/src/common/definitions/index.ts +3 -0
  105. package/src/common/utils/base64UrlEncode/base64UrlEncode.method.ts +4 -0
  106. package/src/common/utils/base64UrlEncode/index.ts +1 -0
  107. package/src/common/utils/getNested/getNested.definitions.ts +4 -0
  108. package/src/common/utils/getNested/getNested.method.ts +108 -0
  109. package/src/common/utils/getNested/getNested.spec.ts +151 -0
  110. package/src/common/utils/getNested/index.ts +2 -0
  111. package/src/common/utils/httpRequest/httpRequest.definitions.ts +22 -0
  112. package/src/common/utils/httpRequest/httpRequest.method.ts +46 -0
  113. package/src/common/utils/httpRequest/index.ts +2 -0
  114. package/src/common/utils/index.ts +5 -0
  115. package/src/common/utils/loadDynamicModules/index.ts +1 -0
  116. package/src/common/utils/loadDynamicModules/utils.loadDynamicModules.spec.ts +111 -0
  117. package/src/common/utils/loadDynamicModules/utils.loadDynamicModules.ts +69 -0
  118. package/src/common/utils/setNested/index.ts +2 -0
  119. package/src/common/utils/setNested/setNested.definitions.ts +4 -0
  120. package/src/common/utils/setNested/setNested.method.ts +83 -0
  121. package/src/common/utils/setNested/setNested.spec.ts +184 -0
  122. package/src/data/entityService/data.entity.service.definitions.ts +154 -0
  123. package/src/data/entityService/data.entity.service.spec.ts +112 -0
  124. package/src/data/entityService/data.entity.service.ts +144 -0
  125. package/src/data/entityService/index.ts +2 -0
  126. package/src/domain/entityService/domain.entity.service.definitions.ts +142 -0
  127. package/src/domain/entityService/domain.entity.service.spec.ts +126 -0
  128. package/src/domain/entityService/domain.entity.service.ts +421 -0
  129. package/src/domain/entityService/index.ts +2 -0
  130. package/src/index.ts +6 -0
  131. package/src/vitest.config.ts +9 -0
  132. package/dist/common/utils/utils.loadDynamicModules.js.map +0 -1
  133. package/dist/persistance/common/entityService/index.d.ts +0 -2
  134. package/dist/persistance/common/entityService/index.js.map +0 -1
  135. package/dist/persistance/common/entityService/persistance.entity.service.d.ts +0 -11
  136. package/dist/persistance/common/entityService/persistance.entity.service.definitions.d.ts +0 -70
  137. package/dist/persistance/common/entityService/persistance.entity.service.definitions.js +0 -23
  138. package/dist/persistance/common/entityService/persistance.entity.service.definitions.js.map +0 -1
  139. package/dist/persistance/common/entityService/persistance.entity.service.js.map +0 -1
  140. /package/dist/common/utils/{utils.loadDynamicModules.js → loadDynamicModules/utils.loadDynamicModules.js} +0 -0
@@ -0,0 +1,142 @@
1
+ import { GenericObject } from '../../common/definitions';
2
+
3
+ import {
4
+ DataBulkCreatePrivateOptions,
5
+ DataCreatePrivateOptions,
6
+ DataDeleteOptions,
7
+ DataDeletePrivateOptions,
8
+ DataDeleteResult,
9
+ DataFindOneOptions,
10
+ DataFindOnePrivateOptions,
11
+ DataFindOptions,
12
+ DataFindPrivateOptions,
13
+ DataFindResults,
14
+ DataUpdateOptions,
15
+ DataUpdatePrivateOptions,
16
+ DataUpdateResult
17
+ } from '../../data/entityService';
18
+
19
+ export interface DomainBaseAdditionalServiceOptionsOverrides {
20
+ filterByFirstServiceResultFields?: GenericObject<string>;
21
+ returnData?: boolean;
22
+ runOnNoFirstServiceResultOnly?: boolean | string;
23
+ }
24
+
25
+ export type DomainBaseOptions<Options> = Options & DomainBaseOptionsForAdditionalServices<Options>;
26
+
27
+ export interface DomainBaseOptionsForAdditionalServices<Options> {
28
+ optionsOverridesByService?: GenericObject<Partial<Options> & DomainBaseAdditionalServiceOptionsOverrides>;
29
+ dataServices?: DomainDataServicesKey[];
30
+ }
31
+
32
+ export type DomainBaseOptionsForAdditionalServicesFull<
33
+ Options extends object | undefined = undefined,
34
+ SaveAdditionalResultsOptions extends object | undefined = undefined
35
+ > = DomainBaseOptionsForAdditionalServices<Options> & DomainBaseOptionsWithSearchData<SaveAdditionalResultsOptions>;
36
+
37
+ export interface DomainBaseOptionsWithSearchData<SaveAdditionalResultsOptions extends object | undefined = undefined> {
38
+ saveAdditionalResultsInFirstService?: {
39
+ saveOptions?: SaveAdditionalResultsOptions;
40
+ serviceName: string;
41
+ useResultsForFirstService?: boolean;
42
+ };
43
+ }
44
+
45
+ export interface DomainBaseResult<Result> {
46
+ result: Result;
47
+ resultsByService?: GenericObject<Result>;
48
+ }
49
+
50
+ export type DomainBulkCreatePrivateOptions = DataBulkCreatePrivateOptions;
51
+
52
+ export type DomainBulkCreateData<Entity> = Partial<Entity>[];
53
+
54
+ export type DomainBulkCreateOptions<Options = object> = DomainBaseOptions<Options>;
55
+
56
+ export type DomainBulkCreateResult<Entity> = DomainBaseResult<Entity[]>;
57
+
58
+ export type DomainCreateData<Entity> = Partial<Entity>;
59
+
60
+ export type DomainCreateOptions<Options = object> = DomainBaseOptions<Options>;
61
+
62
+ export type DomainCreatePrivateOptions = DataCreatePrivateOptions;
63
+
64
+ export type DomainCreateResult<Entity> = DomainBaseResult<Entity>;
65
+
66
+ export type DomainDeleteOptions<Options = object> = Options & DomainBaseOptions<DataDeleteOptions>;
67
+
68
+ export type DomainDeletePrivateOptions = DataDeletePrivateOptions;
69
+
70
+ export type DomainDeleteResult<Entity> = DomainBaseResult<DataDeleteResult<Entity>>;
71
+
72
+ export interface DomainEntityServiceDefaultData<Entity> {
73
+ BulkCreate: DomainBulkCreateData<Entity>;
74
+ Create: DomainCreateData<Entity>;
75
+ Update: DomainUpdateData<Entity>;
76
+ }
77
+
78
+ export type DomainFindOneOptions<Options = object> = Options &
79
+ DomainBaseOptions<DataFindOneOptions> &
80
+ DomainBaseOptionsWithSearchData<DomainCreateOptions>;
81
+
82
+ export type DomainFindOnePrivateOptions = DataFindOnePrivateOptions;
83
+
84
+ export type DomainFindOneResult<Entity> = DomainBaseResult<Entity | null>;
85
+
86
+ export type DomainFindOptions<Options = object> = Options &
87
+ DomainBaseOptions<DataFindOptions> &
88
+ DomainBaseOptionsWithSearchData<DomainBulkCreateOptions>;
89
+
90
+ export type DomainFindPrivateOptions = DataFindPrivateOptions;
91
+
92
+ export type DomainFindResult<Entity> = DomainBaseResult<DataFindResults<Entity>>;
93
+
94
+ export enum DomainMethod {
95
+ // eslint-disable-next-line no-unused-vars
96
+ BulkCreate = 'bulkCreate',
97
+ // eslint-disable-next-line no-unused-vars
98
+ Create = 'create',
99
+ // eslint-disable-next-line no-unused-vars
100
+ Delete = 'delete',
101
+ // eslint-disable-next-line no-unused-vars
102
+ Find = 'find',
103
+ // eslint-disable-next-line no-unused-vars
104
+ FindOne = 'findOne',
105
+ // eslint-disable-next-line no-unused-vars
106
+ Update = 'update'
107
+ }
108
+
109
+ export const DOMAIN_ENTITY_SERVICE_DEFAULT_METHODS = [
110
+ DomainMethod.BulkCreate,
111
+ DomainMethod.Create,
112
+ DomainMethod.Delete,
113
+ DomainMethod.Find,
114
+ DomainMethod.FindOne,
115
+ DomainMethod.Update
116
+ ];
117
+
118
+ export enum DomainDataEntityServiceType {
119
+ // eslint-disable-next-line no-unused-vars
120
+ All = 'all',
121
+ // eslint-disable-next-line no-unused-vars
122
+ Main = 'main'
123
+ }
124
+
125
+ export type DomainDataServicesKey = DomainDataEntityServiceType | string;
126
+
127
+ export type DomainRunMethodInAdditionalServicesOptions<Options> = {
128
+ firstServiceResult?: unknown;
129
+ hasFirstServiceResult: boolean;
130
+ methodArgs?: unknown[];
131
+ methodName: string;
132
+ optionsArgIndex?: number;
133
+ optionsOverridesByService?: GenericObject<Partial<Options> & DomainBaseAdditionalServiceOptionsOverrides>;
134
+ };
135
+
136
+ export type DomainUpdateData<Entity> = Partial<Entity>;
137
+
138
+ export type DomainUpdateOptions<Options = object> = Options & DomainBaseOptions<DataUpdateOptions>;
139
+
140
+ export type DomainUpdatePrivateOptions = DataUpdatePrivateOptions;
141
+
142
+ export type DomainUpdateResult<Entity> = DomainBaseResult<DataUpdateResult<Entity>>;
@@ -0,0 +1,126 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ import { DomainEntityService } from './domain.entity.service';
4
+
5
+ import {
6
+ DataDeleteOptions,
7
+ DataDeleteResult,
8
+ DataEntityService,
9
+ DataFindOneOptions,
10
+ DataFindOptions,
11
+ DataFindResults,
12
+ DataUpdateOptions,
13
+ DataUpdateResult
14
+ } from '../../data/entityService';
15
+
16
+ interface TestEntity {
17
+ id: number;
18
+ name: string;
19
+ }
20
+
21
+ describe('DomainEntityService', () => {
22
+ let mockDataService: DataEntityService<TestEntity>;
23
+ let domainService: DomainEntityService<TestEntity, DataEntityService<TestEntity>>;
24
+
25
+ beforeEach(() => {
26
+ // Create a fully typed mock for the underlying persistence service.
27
+ mockDataService = {
28
+ bulkCreate: vi.fn().mockResolvedValue([{ id: 1, name: 'Test1' }]),
29
+ create: vi.fn().mockResolvedValue({ id: 2, name: 'Test2' }),
30
+ count: vi.fn().mockResolvedValue({ count: 0 }),
31
+ delete: vi.fn().mockResolvedValue({ affected: 1 } as DataDeleteResult),
32
+ find: vi.fn().mockResolvedValue({ items: [{ id: 3, name: 'Test3' }] } as DataFindResults<TestEntity>),
33
+ findOne: vi.fn().mockResolvedValue({ id: 4, name: 'Test4' }),
34
+ getEntityName: vi.fn().mockResolvedValue('mockEntity'),
35
+ update: vi.fn().mockResolvedValue({ updated: { id: 5, name: 'Test5' } } as DataUpdateResult<TestEntity>)
36
+ };
37
+ domainService = new DomainEntityService<TestEntity, DataEntityService<TestEntity>>(mockDataService);
38
+ });
39
+
40
+ describe('bulkCreate', () => {
41
+ it('should call dataEntityService.bulkCreate with the provided data and return its result', async () => {
42
+ const data: TestEntity[] = [{ id: 10, name: 'BulkTest' }];
43
+ const result = await domainService.bulkCreate(data);
44
+ expect(mockDataService.bulkCreate).toHaveBeenCalledWith(data);
45
+ expect(result).toEqual({ result: [{ id: 1, name: 'Test1' }] });
46
+ });
47
+ it('should propagate errors from bulkCreate', async () => {
48
+ const error = new Error('bulk error');
49
+ (mockDataService.bulkCreate as ReturnType<typeof vi.fn>).mockRejectedValueOnce(error);
50
+ await expect(domainService.bulkCreate([{ id: 0, name: 'Error' }])).rejects.toThrow('bulk error');
51
+ });
52
+ });
53
+
54
+ describe('create', () => {
55
+ it('should call dataEntityService.create with the provided data and return its result', async () => {
56
+ const data: TestEntity = { id: 20, name: 'CreateTest' };
57
+ const result = await domainService.create(data);
58
+ expect(mockDataService.create).toHaveBeenCalledWith(data);
59
+ expect(result).toEqual({ result: { id: 2, name: 'Test2' } });
60
+ });
61
+ it('should propagate errors from create', async () => {
62
+ const error = new Error('create error');
63
+ (mockDataService.create as ReturnType<typeof vi.fn>).mockRejectedValueOnce(error);
64
+ await expect(domainService.create({ id: 0, name: 'Error' })).rejects.toThrow('create error');
65
+ });
66
+ });
67
+
68
+ describe('delete', () => {
69
+ it('should call dataEntityService.delete with correct options and return its result', async () => {
70
+ const options: DataDeleteOptions = { filters: { id: 1 } };
71
+ const result = await domainService.delete(options);
72
+ expect(mockDataService.delete).toHaveBeenCalledWith(options);
73
+ expect(result).toEqual({ result: { affected: 1 } });
74
+ });
75
+ it('should propagate errors from delete', async () => {
76
+ const error = new Error('delete error');
77
+ (mockDataService.delete as ReturnType<typeof vi.fn>).mockRejectedValueOnce(error);
78
+ await expect(domainService.delete({ filters: { id: 999 } })).rejects.toThrow('delete error');
79
+ });
80
+ });
81
+
82
+ describe('find', () => {
83
+ it('should call dataEntityService.find with correct options and return its result', async () => {
84
+ const options: DataFindOptions = { filters: { name: 'Test' } };
85
+ const result = await domainService.find(options);
86
+ expect(mockDataService.find).toHaveBeenCalledWith(options);
87
+ expect(result).toEqual({ result: { items: [{ id: 3, name: 'Test3' }] } });
88
+ });
89
+ it('should propagate errors from find', async () => {
90
+ const error = new Error('find error');
91
+ (mockDataService.find as ReturnType<typeof vi.fn>).mockRejectedValueOnce(error);
92
+ await expect(domainService.find({ filters: { name: 'Error' } })).rejects.toThrow('find error');
93
+ });
94
+ });
95
+
96
+ describe('findOne', () => {
97
+ it('should call dataEntityService.findOne with correct options and return its result', async () => {
98
+ const options: DataFindOneOptions = { filters: { id: 4 } };
99
+ const result = await domainService.findOne(options);
100
+ expect(mockDataService.findOne).toHaveBeenCalledWith(options);
101
+ expect(result).toEqual({ result: { id: 4, name: 'Test4' } });
102
+ });
103
+ it('should propagate errors from findOne', async () => {
104
+ const error = new Error('findOne error');
105
+ (mockDataService.findOne as ReturnType<typeof vi.fn>).mockRejectedValueOnce(error);
106
+ await expect(domainService.findOne({ filters: { id: 999 } })).rejects.toThrow('findOne error');
107
+ });
108
+ });
109
+
110
+ describe('update', () => {
111
+ it('should call dataEntityService.update with correct data and options and return its result', async () => {
112
+ const data: TestEntity = { id: 30, name: 'UpdateTest' };
113
+ const options: DataUpdateOptions = { filters: { id: 30 } };
114
+ const result = await domainService.update(data, options);
115
+ expect(mockDataService.update).toHaveBeenCalledWith(data, options);
116
+ expect(result).toEqual({ result: { updated: { id: 5, name: 'Test5' } } });
117
+ });
118
+ it('should propagate errors from update', async () => {
119
+ const error = new Error('update error');
120
+ (mockDataService.update as ReturnType<typeof vi.fn>).mockRejectedValueOnce(error);
121
+ await expect(domainService.update({ id: 0, name: 'Error' }, { filters: { id: 0 } })).rejects.toThrow(
122
+ 'update error'
123
+ );
124
+ });
125
+ });
126
+ });
@@ -0,0 +1,421 @@
1
+ import ld from 'lodash';
2
+
3
+ import {
4
+ DOMAIN_ENTITY_SERVICE_DEFAULT_METHODS,
5
+ DomainBaseAdditionalServiceOptionsOverrides,
6
+ DomainBulkCreateOptions,
7
+ DomainBulkCreatePrivateOptions,
8
+ DomainBulkCreateResult,
9
+ DomainCreateOptions,
10
+ DomainCreatePrivateOptions,
11
+ DomainCreateResult,
12
+ DomainDataEntityServiceType,
13
+ DomainDeleteOptions,
14
+ DomainDeletePrivateOptions,
15
+ DomainDeleteResult,
16
+ DomainEntityServiceDefaultData,
17
+ DomainFindOneOptions,
18
+ DomainFindOnePrivateOptions,
19
+ DomainFindOneResult,
20
+ DomainFindOptions,
21
+ DomainFindPrivateOptions,
22
+ DomainFindResult,
23
+ DomainMethod,
24
+ DomainRunMethodInAdditionalServicesOptions,
25
+ DomainUpdateOptions,
26
+ DomainUpdatePrivateOptions,
27
+ DomainUpdateResult
28
+ } from './domain.entity.service.definitions';
29
+
30
+ import { ApplicationError, GenericObject } from '../../common/definitions';
31
+
32
+ import { DataDefaultData, DataEntityService, DataFindResults } from '../../data/entityService';
33
+
34
+ // TODO: privateOptionsOverrides by service
35
+ export class DomainEntityService<
36
+ Entity,
37
+ EntityService extends DataEntityService<Entity, DataEntityServiceData>,
38
+ Data extends DomainEntityServiceDefaultData<Entity> = DomainEntityServiceDefaultData<Entity>,
39
+ AdditionalEntityServices extends
40
+ | Record<string, DataEntityService<Partial<Entity>, DataDefaultData<object>>>
41
+ | undefined = undefined,
42
+ DataEntityServiceData extends DataDefaultData<Entity> = DataDefaultData<Entity>
43
+ > {
44
+ constructor(
45
+ // eslint-disable-next-line no-unused-vars
46
+ protected dataEntityService: EntityService,
47
+ // eslint-disable-next-line no-unused-vars
48
+ protected defaultMethods: string[] = DOMAIN_ENTITY_SERVICE_DEFAULT_METHODS,
49
+ // eslint-disable-next-line no-unused-vars
50
+ protected additionalDataEntityServices?: AdditionalEntityServices,
51
+ // eslint-disable-next-line no-unused-vars
52
+ protected defaultAdditionalDataEntityServicesOptions?: {
53
+ [methodName: string]: {
54
+ [serviceName: string]: {
55
+ allowIncoming?: boolean;
56
+ serviceOptions?: DomainBaseAdditionalServiceOptionsOverrides & GenericObject<unknown>;
57
+ };
58
+ };
59
+ }
60
+ ) {}
61
+
62
+ public bulkCreate(
63
+ // eslint-disable-next-line no-unused-vars
64
+ data: Data['BulkCreate'],
65
+ // eslint-disable-next-line no-unused-vars
66
+ options?: DomainBulkCreateOptions,
67
+ // eslint-disable-next-line no-unused-vars
68
+ privateOptions?: DomainBulkCreatePrivateOptions
69
+ ): Promise<DomainBulkCreateResult<Entity>>;
70
+ async bulkCreate(
71
+ data: Data['BulkCreate'],
72
+ options?: DomainBulkCreateOptions,
73
+ privateOptions?: DomainBulkCreatePrivateOptions
74
+ ): Promise<DomainBulkCreateResult<Entity>> {
75
+ if (!this.defaultMethods?.includes(DomainMethod.BulkCreate)) {
76
+ throw new ApplicationError(`Method bulkCreate not implemented for class ${typeof this}.`);
77
+ }
78
+ // const defaultAdditionalDataEntityServicesOptions =
79
+ // this.defaultAdditionalDataEntityServicesOptions?.bulkCreate;
80
+ const { optionsOverridesByService, dataServices = [DomainDataEntityServiceType.Main] } = options || {};
81
+ const [firstServiceName, ...otherServiceNames] = dataServices;
82
+ const result = await this.getDataService(firstServiceName).bulkCreate(data, privateOptions);
83
+ let actualOtherServiceNames: string[] = [];
84
+ let actualOptionsOverridesByService: typeof optionsOverridesByService = {};
85
+ // if (defaultAdditionalDataEntityServicesOptions) {
86
+ // for (const serviceName in defaultAdditionalDataEntityServicesOptions) {
87
+ // const { allowIncoming = true, serviceOptions } = defaultAdditionalDataEntityServicesOptions[serviceName];
88
+ // }
89
+ // } else {
90
+ actualOtherServiceNames = otherServiceNames || [];
91
+ actualOptionsOverridesByService = optionsOverridesByService;
92
+ // }
93
+ return {
94
+ result,
95
+ resultsByService: await this.runMethodInAdditionalServices(actualOtherServiceNames, {
96
+ firstServiceResult: result,
97
+ hasFirstServiceResult: result.length > 0,
98
+ methodArgs: [result, privateOptions],
99
+ methodName: 'bulkCreate',
100
+ optionsArgIndex: 1,
101
+ optionsOverridesByService: actualOptionsOverridesByService
102
+ })
103
+ };
104
+ }
105
+
106
+ public create(
107
+ // eslint-disable-next-line no-unused-vars
108
+ data: Data['Create'],
109
+ // eslint-disable-next-line no-unused-vars
110
+ options?: DomainCreateOptions,
111
+ // eslint-disable-next-line no-unused-vars
112
+ privateOptions?: DomainCreatePrivateOptions
113
+ ): Promise<DomainCreateResult<Entity>>;
114
+ async create<Options extends object | undefined = undefined>(
115
+ data: Data['Create'],
116
+ options?: DomainCreateOptions<Options>,
117
+ privateOptions?: DomainCreatePrivateOptions
118
+ ): Promise<DomainCreateResult<Entity>> {
119
+ if (!this.defaultMethods?.includes(DomainMethod.Create)) {
120
+ throw new ApplicationError(`Method create not implemented for class ${typeof this}.`);
121
+ }
122
+ const { optionsOverridesByService, dataServices = [DomainDataEntityServiceType.Main] } = options || {};
123
+ const [firstServiceName, ...otherServiceNames] = dataServices;
124
+ const result = await this.getDataService(firstServiceName).create(data, privateOptions);
125
+ return {
126
+ result,
127
+ resultsByService: await this.runMethodInAdditionalServices(otherServiceNames || [], {
128
+ firstServiceResult: result,
129
+ hasFirstServiceResult: typeof result !== 'undefined' && result !== null,
130
+ methodArgs: [result, privateOptions],
131
+ methodName: 'create',
132
+ optionsArgIndex: 1,
133
+ optionsOverridesByService
134
+ })
135
+ };
136
+ }
137
+
138
+ public delete(
139
+ // eslint-disable-next-line no-unused-vars
140
+ options: DomainDeleteOptions,
141
+ // eslint-disable-next-line no-unused-vars
142
+ privateOptions?: DomainDeletePrivateOptions
143
+ ): Promise<DomainDeleteResult<Entity>>;
144
+ async delete(
145
+ options: DomainDeleteOptions,
146
+ privateOptions?: DomainDeletePrivateOptions
147
+ ): Promise<DomainDeleteResult<Entity>> {
148
+ if (!this.defaultMethods?.includes(DomainMethod.Delete)) {
149
+ throw new ApplicationError(`Method delete not implemented for class ${typeof this}.`);
150
+ }
151
+ const {
152
+ optionsOverridesByService,
153
+ dataServices = [DomainDataEntityServiceType.Main],
154
+ ...otherOptions
155
+ } = options || {};
156
+ const [firstServiceName, ...otherServiceNames] = dataServices;
157
+ const result = await this.getDataService(firstServiceName).delete(otherOptions, privateOptions);
158
+ return {
159
+ result,
160
+ resultsByService: await this.runMethodInAdditionalServices(otherServiceNames || [], {
161
+ firstServiceResult: { ...result, items: result.originalItems || [] },
162
+ hasFirstServiceResult: !!result.count,
163
+ methodArgs: [otherOptions, privateOptions],
164
+ methodName: 'delete',
165
+ optionsArgIndex: 0,
166
+ optionsOverridesByService
167
+ })
168
+ };
169
+ }
170
+
171
+ public find(
172
+ // eslint-disable-next-line no-unused-vars
173
+ options: DomainFindOptions,
174
+ // eslint-disable-next-line no-unused-vars
175
+ privateOptions?: DomainFindOnePrivateOptions
176
+ ): Promise<DomainFindResult<Entity>>;
177
+ async find(
178
+ options: DomainFindOptions,
179
+ privateOptions?: DomainFindOnePrivateOptions
180
+ ): Promise<DomainFindResult<Entity>> {
181
+ if (!this.defaultMethods?.includes(DomainMethod.Find)) {
182
+ throw new ApplicationError(`Method find not implemented for class ${typeof this}.`);
183
+ }
184
+ const {
185
+ optionsOverridesByService,
186
+ dataServices = [DomainDataEntityServiceType.Main],
187
+ saveAdditionalResultsInFirstService,
188
+ ...otherOptions
189
+ } = options || {};
190
+ const [firstServiceName, ...otherServiceNames] = dataServices;
191
+ let result = await this.getDataService(firstServiceName).find(otherOptions, privateOptions);
192
+ const hasFirstServiceResult = result.items.length > 0;
193
+ const resultsByService = await this.runMethodInAdditionalServices<DataFindResults<Entity>>(
194
+ otherServiceNames || [],
195
+ {
196
+ firstServiceResult: result,
197
+ hasFirstServiceResult,
198
+ methodArgs: [otherOptions, privateOptions],
199
+ methodName: 'find',
200
+ optionsArgIndex: 0,
201
+ optionsOverridesByService
202
+ }
203
+ );
204
+ if (saveAdditionalResultsInFirstService && resultsByService) {
205
+ const { saveOptions, serviceName, useResultsForFirstService } = saveAdditionalResultsInFirstService;
206
+ const dataFromAdditionalService = resultsByService[serviceName];
207
+ if (dataFromAdditionalService?.items?.length) {
208
+ await this.dataEntityService.bulkCreate(dataFromAdditionalService.items, saveOptions);
209
+ if (useResultsForFirstService && !hasFirstServiceResult) {
210
+ result = dataFromAdditionalService;
211
+ }
212
+ }
213
+ }
214
+ return {
215
+ result,
216
+ resultsByService
217
+ };
218
+ }
219
+
220
+ public findOne(
221
+ // eslint-disable-next-line no-unused-vars
222
+ options: DomainFindOneOptions,
223
+ // eslint-disable-next-line no-unused-vars
224
+ privateOptions?: DomainFindPrivateOptions
225
+ ): Promise<DomainFindOneResult<Entity>>;
226
+ async findOne(
227
+ options: DomainFindOneOptions,
228
+ privateOptions?: DomainFindPrivateOptions
229
+ ): Promise<DomainFindOneResult<Entity>> {
230
+ if (!this.defaultMethods?.includes(DomainMethod.FindOne)) {
231
+ throw new ApplicationError(`Method findOne not implemented for class ${typeof this}.`);
232
+ }
233
+ const {
234
+ optionsOverridesByService,
235
+ dataServices = [DomainDataEntityServiceType.Main],
236
+ saveAdditionalResultsInFirstService,
237
+ ...otherOptions
238
+ } = options || {};
239
+ const [firstServiceName, ...otherServiceNames] = dataServices;
240
+ let result: Entity | null = await this.getDataService(firstServiceName).findOne(otherOptions, privateOptions);
241
+ const hasFirstServiceResult = typeof result !== 'undefined' && result !== null;
242
+ const resultsByService = await this.runMethodInAdditionalServices<Entity | null>(otherServiceNames || [], {
243
+ firstServiceResult: result,
244
+ hasFirstServiceResult,
245
+ methodArgs: [otherOptions, privateOptions],
246
+ methodName: 'findOne',
247
+ optionsArgIndex: 0,
248
+ optionsOverridesByService
249
+ });
250
+ if (saveAdditionalResultsInFirstService && resultsByService) {
251
+ const { saveOptions, serviceName, useResultsForFirstService } = saveAdditionalResultsInFirstService;
252
+ const dataFromAdditionalService = resultsByService[serviceName];
253
+ if (dataFromAdditionalService) {
254
+ await this.dataEntityService.create(dataFromAdditionalService, saveOptions);
255
+ if (useResultsForFirstService && !hasFirstServiceResult) {
256
+ result = dataFromAdditionalService;
257
+ }
258
+ }
259
+ }
260
+ return {
261
+ result,
262
+ resultsByService
263
+ };
264
+ }
265
+
266
+ protected getDataService(serviceName: DomainDataEntityServiceType.Main | string): DataEntityService<Entity> {
267
+ if (serviceName === DomainDataEntityServiceType.Main) {
268
+ return this.dataEntityService;
269
+ }
270
+ const service = this.additionalDataEntityServices?.[serviceName];
271
+ if (!service) {
272
+ throw new ApplicationError(
273
+ `DataEntityService ${serviceName} does not exist for DomainEntityService ${this.dataEntityService.getEntityName(true) || '(no entity name)'}.`
274
+ );
275
+ }
276
+ return service as DataEntityService<Entity>;
277
+ }
278
+
279
+ protected async runMethodInAdditionalServices<ServiceReturnData>(
280
+ serviceNames: string[],
281
+ options: DomainRunMethodInAdditionalServicesOptions<unknown>
282
+ ): Promise<GenericObject<ServiceReturnData> | undefined> {
283
+ if (!serviceNames.length) {
284
+ return undefined;
285
+ }
286
+ const {
287
+ firstServiceResult,
288
+ hasFirstServiceResult,
289
+ methodArgs = [],
290
+ methodName,
291
+ optionsArgIndex,
292
+ optionsOverridesByService = {}
293
+ } = options;
294
+ const returnDataByService: GenericObject<ServiceReturnData> = {};
295
+ if (!this.additionalDataEntityServices) {
296
+ throw new ApplicationError(
297
+ `No additional DataEntityServices exist for DomainEntityService ${this.dataEntityService.getEntityName(true) || '(no entity name)'}.`
298
+ );
299
+ }
300
+ if (
301
+ Object.keys(optionsOverridesByService).length &&
302
+ (typeof optionsArgIndex === 'undefined' || optionsArgIndex < 0 || optionsArgIndex > methodArgs.length - 1)
303
+ ) {
304
+ throw new ApplicationError(
305
+ `Invalid optionsArgIndex value ${optionsArgIndex} provided for DomainEntityService ${this.dataEntityService.getEntityName(true) || '(no entity name)'}.}.`
306
+ );
307
+ }
308
+ for (const i in serviceNames) {
309
+ const serviceName = serviceNames[i];
310
+ const service = this.getDataService(serviceName);
311
+ if (!service) {
312
+ throw new ApplicationError(
313
+ `DataEntityService ${serviceName} does not exist for DomainEntityService ${this.dataEntityService.getEntityName(true) || '(no entity name)'}.`
314
+ );
315
+ }
316
+ const serviceMethodOptionsOverrides = optionsOverridesByService[serviceName] || {};
317
+ const {
318
+ filterByFirstServiceResultFields,
319
+ runOnNoFirstServiceResultOnly = true,
320
+ ...actualMethodOptionsOverrides
321
+ } = serviceMethodOptionsOverrides;
322
+ // be extra careful when working with data that has TTL as the main service,
323
+ // since there is no way to check for limited results here.
324
+ if (
325
+ (runOnNoFirstServiceResultOnly === true || runOnNoFirstServiceResultOnly === 'true') &&
326
+ hasFirstServiceResult
327
+ ) {
328
+ continue;
329
+ }
330
+ const serviceMethodArgs = ld.cloneDeep(methodArgs);
331
+ if (typeof serviceMethodArgs[optionsArgIndex!] === 'undefined') {
332
+ if (optionsArgIndex! > serviceMethodArgs.length - 1) {
333
+ serviceMethodArgs.push(actualMethodOptionsOverrides);
334
+ } else {
335
+ serviceMethodArgs[optionsArgIndex!] = actualMethodOptionsOverrides;
336
+ }
337
+ } else {
338
+ serviceMethodArgs[optionsArgIndex!] = {
339
+ ...(serviceMethodArgs[optionsArgIndex!] as object),
340
+ ...actualMethodOptionsOverrides
341
+ };
342
+ }
343
+ if (filterByFirstServiceResultFields && Object.keys(filterByFirstServiceResultFields).length) {
344
+ if (!hasFirstServiceResult) {
345
+ continue;
346
+ }
347
+ const filters: GenericObject = {};
348
+ const resultItems: GenericObject[] = (firstServiceResult as { items?: GenericObject[] }).items || [
349
+ firstServiceResult as GenericObject
350
+ ];
351
+ resultItems.forEach(resultItem => {
352
+ if (!resultItem) {
353
+ return;
354
+ }
355
+ for (const sourceFieldName in filterByFirstServiceResultFields) {
356
+ const fieldValue = resultItem[sourceFieldName];
357
+ const targetFieldName = filterByFirstServiceResultFields[sourceFieldName];
358
+ if (typeof fieldValue === 'undefined') {
359
+ return;
360
+ }
361
+ if (!filters[targetFieldName]) {
362
+ filters[targetFieldName] = [];
363
+ }
364
+ (filters[targetFieldName] as unknown[]).push(fieldValue);
365
+ }
366
+ });
367
+ if (Object.keys(filters).length) {
368
+ const serviceMethodOptions = serviceMethodArgs[optionsArgIndex!] as GenericObject & {
369
+ filters?: GenericObject;
370
+ };
371
+ serviceMethodArgs[optionsArgIndex!] = {
372
+ ...serviceMethodOptions,
373
+ filters: {
374
+ ...ld.omit(serviceMethodOptions.filters || {}, ['page', 'perPage']),
375
+ ...filters
376
+ },
377
+ findAll: true
378
+ };
379
+ }
380
+ }
381
+ returnDataByService[serviceName] = (await (
382
+ service[methodName as keyof DataEntityService<Entity>] as unknown as (
383
+ ..._args: unknown[]
384
+ ) => Promise<ServiceReturnData>
385
+ ).apply(service, serviceMethodArgs)) as ServiceReturnData;
386
+ }
387
+ return returnDataByService;
388
+ }
389
+
390
+ public update(
391
+ // eslint-disable-next-line no-unused-vars
392
+ data: Data['Update'],
393
+ // eslint-disable-next-line no-unused-vars
394
+ options: DomainUpdateOptions,
395
+ // eslint-disable-next-line no-unused-vars
396
+ privateOptions?: DomainUpdatePrivateOptions
397
+ ): Promise<DomainUpdateResult<Entity>>;
398
+ async update(
399
+ data: Data['Update'],
400
+ options: DomainUpdateOptions,
401
+ privateOptions?: DomainUpdatePrivateOptions
402
+ ): Promise<DomainUpdateResult<Entity>> {
403
+ if (!this.defaultMethods?.includes(DomainMethod.Update)) {
404
+ throw new ApplicationError(`Method update not implemented for class ${typeof this}.`);
405
+ }
406
+ const { optionsOverridesByService, dataServices = [DomainDataEntityServiceType.Main], ...otherOptions } = options;
407
+ const [firstServiceName, ...otherServiceNames] = dataServices;
408
+ const result = await this.getDataService(firstServiceName).update(data, otherOptions, privateOptions);
409
+ return {
410
+ result,
411
+ resultsByService: await this.runMethodInAdditionalServices(otherServiceNames || [], {
412
+ firstServiceResult: result,
413
+ hasFirstServiceResult: !!result.count,
414
+ methodArgs: [data, otherOptions, privateOptions],
415
+ methodName: 'update',
416
+ optionsArgIndex: 1,
417
+ optionsOverridesByService
418
+ })
419
+ };
420
+ }
421
+ }
@@ -0,0 +1,2 @@
1
+ export * from './domain.entity.service';
2
+ export * from './domain.entity.service.definitions';