@flusys/nestjs-shared 1.1.0-beta → 1.2.0-rc.1

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 (198) hide show
  1. package/README.md +146 -1330
  2. package/cjs/classes/api-controller.class.js +325 -49
  3. package/cjs/classes/api-service.class.js +271 -149
  4. package/cjs/classes/handle-custome-db.js +37 -0
  5. package/cjs/classes/index.js +2 -2
  6. package/cjs/classes/winston.logger.class.js +103 -70
  7. package/cjs/constants/index.js +15 -4
  8. package/cjs/constants/message-keys.js +68 -0
  9. package/cjs/constants/permissions.js +287 -0
  10. package/cjs/decorators/api-response.decorator.js +2 -3
  11. package/cjs/decorators/index.js +1 -0
  12. package/cjs/decorators/log-action.decorator.js +149 -0
  13. package/cjs/decorators/require-permission.decorator.js +5 -5
  14. package/cjs/dtos/delete.dto.js +10 -0
  15. package/cjs/dtos/filter-and-pagination.dto.js +24 -34
  16. package/cjs/dtos/get-by-ids.dto.js +65 -0
  17. package/cjs/dtos/identity-response.dto.js +4 -8
  18. package/cjs/dtos/index.js +1 -0
  19. package/cjs/dtos/pagination.dto.js +4 -8
  20. package/cjs/dtos/response-payload.dto.js +68 -112
  21. package/cjs/entities/identity.js +12 -6
  22. package/cjs/entities/index.js +1 -0
  23. package/cjs/entities/raw-type.js +4 -0
  24. package/cjs/entities/user-root.js +15 -15
  25. package/cjs/enums/index.js +20 -0
  26. package/cjs/enums/notification-type.enum.js +17 -0
  27. package/cjs/enums/participant-status.enum.js +17 -0
  28. package/cjs/enums/recurrence-type.enum.js +18 -0
  29. package/cjs/exceptions/base-app.exception.js +151 -0
  30. package/cjs/exceptions/index.js +1 -0
  31. package/cjs/exceptions/permission.exception.js +13 -9
  32. package/cjs/filters/global-exception.filter.js +279 -0
  33. package/cjs/filters/index.js +18 -0
  34. package/cjs/guards/jwt-auth.guard.js +4 -1
  35. package/cjs/guards/permission.guard.js +88 -148
  36. package/cjs/index.js +2 -0
  37. package/cjs/interceptors/idempotency.interceptor.js +1 -1
  38. package/cjs/interceptors/index.js +1 -4
  39. package/cjs/interceptors/set-user-field-on-body.interceptor.js +60 -0
  40. package/cjs/interceptors/slug.interceptor.js +30 -9
  41. package/cjs/interfaces/datasource.interface.js +4 -0
  42. package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
  43. package/cjs/interfaces/index.js +4 -2
  44. package/cjs/interfaces/module-config.interface.js +4 -0
  45. package/cjs/interfaces/notification-adapter.interface.js +11 -0
  46. package/cjs/interfaces/permission.interface.js +1 -1
  47. package/cjs/middlewares/logger.middleware.js +133 -115
  48. package/cjs/modules/cache/cache.module.js +3 -3
  49. package/cjs/modules/datasource/index.js +0 -1
  50. package/cjs/modules/datasource/multi-tenant-datasource.service.js +60 -183
  51. package/cjs/modules/utils/utils.service.js +27 -206
  52. package/cjs/utils/date-time.util.js +94 -0
  53. package/cjs/utils/html-sanitizer.util.js +64 -0
  54. package/cjs/utils/index.js +5 -1
  55. package/cjs/utils/query-helpers.util.js +60 -0
  56. package/cjs/utils/request.util.js +96 -0
  57. package/cjs/utils/string.util.js +63 -0
  58. package/classes/api-controller.class.d.ts +16 -8
  59. package/classes/api-service.class.d.ts +30 -25
  60. package/classes/handle-custome-db.d.ts +3 -0
  61. package/classes/index.d.ts +2 -2
  62. package/classes/winston.logger.class.d.ts +1 -0
  63. package/constants/index.d.ts +2 -1
  64. package/constants/message-keys.d.ts +45 -0
  65. package/constants/permissions.d.ts +306 -0
  66. package/decorators/api-response.decorator.d.ts +1 -1
  67. package/decorators/current-user.decorator.d.ts +1 -1
  68. package/decorators/index.d.ts +1 -0
  69. package/decorators/log-action.decorator.d.ts +8 -0
  70. package/decorators/require-permission.decorator.d.ts +2 -2
  71. package/dtos/delete.dto.d.ts +1 -0
  72. package/dtos/filter-and-pagination.dto.d.ts +0 -2
  73. package/dtos/get-by-ids.dto.d.ts +4 -0
  74. package/dtos/index.d.ts +1 -0
  75. package/dtos/response-payload.dto.d.ts +8 -20
  76. package/entities/index.d.ts +1 -0
  77. package/entities/raw-type.d.ts +1 -0
  78. package/enums/index.d.ts +3 -0
  79. package/enums/notification-type.enum.d.ts +6 -0
  80. package/enums/participant-status.enum.d.ts +6 -0
  81. package/enums/recurrence-type.enum.d.ts +7 -0
  82. package/exceptions/base-app.exception.d.ts +41 -0
  83. package/exceptions/index.d.ts +1 -0
  84. package/exceptions/permission.exception.d.ts +2 -2
  85. package/fesm/classes/api-controller.class.js +326 -50
  86. package/fesm/classes/api-service.class.js +274 -152
  87. package/fesm/classes/handle-custome-db.js +19 -0
  88. package/fesm/classes/index.js +3 -2
  89. package/fesm/classes/winston.logger.class.js +100 -68
  90. package/fesm/constants/index.js +4 -1
  91. package/fesm/constants/message-keys.js +49 -0
  92. package/fesm/constants/permissions.js +200 -0
  93. package/fesm/decorators/api-response.decorator.js +3 -21
  94. package/fesm/decorators/index.js +1 -0
  95. package/fesm/decorators/log-action.decorator.js +139 -0
  96. package/fesm/decorators/require-permission.decorator.js +17 -65
  97. package/fesm/dtos/delete.dto.js +12 -2
  98. package/fesm/dtos/filter-and-pagination.dto.js +26 -47
  99. package/fesm/dtos/get-by-ids.dto.js +55 -0
  100. package/fesm/dtos/identity-response.dto.js +4 -8
  101. package/fesm/dtos/index.js +1 -0
  102. package/fesm/dtos/pagination.dto.js +4 -8
  103. package/fesm/dtos/response-payload.dto.js +68 -103
  104. package/fesm/entities/identity.js +12 -6
  105. package/fesm/entities/index.js +1 -0
  106. package/fesm/entities/raw-type.js +1 -0
  107. package/fesm/entities/user-root.js +15 -15
  108. package/fesm/enums/index.js +3 -0
  109. package/fesm/enums/notification-type.enum.js +7 -0
  110. package/fesm/enums/participant-status.enum.js +7 -0
  111. package/fesm/enums/recurrence-type.enum.js +8 -0
  112. package/fesm/exceptions/base-app.exception.js +115 -0
  113. package/fesm/exceptions/index.js +1 -0
  114. package/fesm/exceptions/permission.exception.js +16 -18
  115. package/fesm/filters/global-exception.filter.js +269 -0
  116. package/fesm/filters/index.js +1 -0
  117. package/fesm/guards/jwt-auth.guard.js +5 -2
  118. package/fesm/guards/permission.guard.js +90 -150
  119. package/fesm/index.js +2 -0
  120. package/fesm/interceptors/idempotency.interceptor.js +2 -2
  121. package/fesm/interceptors/index.js +1 -4
  122. package/fesm/interceptors/set-user-field-on-body.interceptor.js +39 -0
  123. package/fesm/interceptors/slug.interceptor.js +31 -10
  124. package/fesm/interfaces/datasource.interface.js +1 -0
  125. package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
  126. package/fesm/interfaces/index.js +4 -2
  127. package/fesm/interfaces/module-config.interface.js +5 -0
  128. package/fesm/interfaces/notification-adapter.interface.js +1 -0
  129. package/fesm/interfaces/permission.interface.js +1 -3
  130. package/fesm/middlewares/logger.middleware.js +133 -109
  131. package/fesm/modules/cache/cache.module.js +2 -2
  132. package/fesm/modules/datasource/index.js +0 -1
  133. package/fesm/modules/datasource/multi-tenant-datasource.service.js +61 -184
  134. package/fesm/modules/utils/utils.service.js +27 -206
  135. package/fesm/utils/date-time.util.js +70 -0
  136. package/fesm/utils/html-sanitizer.util.js +69 -0
  137. package/fesm/utils/index.js +5 -1
  138. package/fesm/utils/query-helpers.util.js +85 -0
  139. package/fesm/utils/request.util.js +81 -0
  140. package/fesm/utils/string.util.js +71 -0
  141. package/filters/global-exception.filter.d.ts +9 -0
  142. package/filters/index.d.ts +1 -0
  143. package/guards/permission.guard.d.ts +4 -6
  144. package/index.d.ts +2 -0
  145. package/interceptors/idempotency.interceptor.d.ts +1 -1
  146. package/interceptors/index.d.ts +1 -4
  147. package/interceptors/set-user-field-on-body.interceptor.d.ts +5 -0
  148. package/interceptors/slug.interceptor.d.ts +2 -1
  149. package/interfaces/api.interface.d.ts +4 -2
  150. package/interfaces/datasource.interface.d.ts +5 -0
  151. package/interfaces/event-manager-adapter.interface.d.ts +43 -0
  152. package/interfaces/identity.interface.d.ts +4 -4
  153. package/interfaces/index.d.ts +4 -2
  154. package/interfaces/logged-user-info.interface.d.ts +0 -2
  155. package/interfaces/module-config.interface.d.ts +6 -0
  156. package/interfaces/notification-adapter.interface.d.ts +21 -0
  157. package/interfaces/permission.interface.d.ts +13 -9
  158. package/middlewares/logger.middleware.d.ts +2 -2
  159. package/modules/datasource/index.d.ts +0 -1
  160. package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -16
  161. package/modules/utils/utils.service.d.ts +3 -14
  162. package/package.json +21 -14
  163. package/utils/date-time.util.d.ts +8 -0
  164. package/utils/html-sanitizer.util.d.ts +2 -0
  165. package/utils/index.d.ts +5 -1
  166. package/utils/query-helpers.util.d.ts +16 -0
  167. package/utils/request.util.d.ts +6 -0
  168. package/utils/string.util.d.ts +2 -0
  169. package/cjs/classes/request-scoped-api.service.js +0 -61
  170. package/cjs/classes/winston-logger-adapter.class.js +0 -121
  171. package/cjs/interceptors/query-performance.interceptor.js +0 -66
  172. package/cjs/interceptors/set-create-by-on-body.interceptor.js +0 -40
  173. package/cjs/interceptors/set-delete-by-on-body.interceptor.js +0 -40
  174. package/cjs/interceptors/set-update-by-on-body.interceptor.js +0 -40
  175. package/cjs/interfaces/base-query.interface.js +0 -6
  176. package/cjs/interfaces/logger.interface.js +0 -7
  177. package/cjs/modules/datasource/datasource.module.js +0 -120
  178. package/cjs/utils/error-handler.util.js +0 -67
  179. package/classes/request-scoped-api.service.d.ts +0 -13
  180. package/classes/winston-logger-adapter.class.d.ts +0 -20
  181. package/fesm/classes/request-scoped-api.service.js +0 -51
  182. package/fesm/classes/winston-logger-adapter.class.js +0 -124
  183. package/fesm/interceptors/query-performance.interceptor.js +0 -56
  184. package/fesm/interceptors/set-create-by-on-body.interceptor.js +0 -30
  185. package/fesm/interceptors/set-delete-by-on-body.interceptor.js +0 -30
  186. package/fesm/interceptors/set-update-by-on-body.interceptor.js +0 -30
  187. package/fesm/interfaces/base-query.interface.js +0 -3
  188. package/fesm/interfaces/logger.interface.js +0 -4
  189. package/fesm/modules/datasource/datasource.module.js +0 -110
  190. package/fesm/utils/error-handler.util.js +0 -59
  191. package/interceptors/query-performance.interceptor.d.ts +0 -8
  192. package/interceptors/set-create-by-on-body.interceptor.d.ts +0 -5
  193. package/interceptors/set-delete-by-on-body.interceptor.d.ts +0 -5
  194. package/interceptors/set-update-by-on-body.interceptor.d.ts +0 -5
  195. package/interfaces/base-query.interface.d.ts +0 -7
  196. package/interfaces/logger.interface.d.ts +0 -7
  197. package/modules/datasource/datasource.module.d.ts +0 -18
  198. package/utils/error-handler.util.d.ts +0 -24
@@ -11,130 +11,129 @@ function _define_property(obj, key, value) {
11
11
  }
12
12
  return obj;
13
13
  }
14
- import { ErrorHandler } from '../utils/error-handler.util';
15
- import { Logger, NotFoundException } from '@nestjs/common';
16
- import { In } from 'typeorm';
17
- /** Generic API service with CRUD operations and caching support */ export class ApiService {
14
+ function _ts_decorate(decorators, target, key, desc) {
15
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
19
+ }
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ import { Injectable, InternalServerErrorException, NotFoundException, Scope } from '@nestjs/common';
24
+ import { EntityTarget, In } from 'typeorm';
25
+ import { ERROR_MESSAGES, SYSTEM_MESSAGES } from '../constants';
26
+ import { LogAction } from '../decorators/log-action.decorator';
27
+ import { DeleteDto } from '../dtos';
28
+ import { IDataSourceProvider } from '../interfaces';
29
+ import { UtilsService } from '../modules/utils/utils.service';
30
+ import { HybridCache } from './hybrid-cache.class';
31
+ export class ApiService {
32
+ resolveEntity() {
33
+ return this._entity;
34
+ }
35
+ getDataSourceProvider() {
36
+ return this._dataSourceProvider;
37
+ }
38
+ async ensureDataSourceRepository() {
39
+ if (!this.repositoryInitialized) {
40
+ const entity = this.resolveEntity();
41
+ const provider = this.getDataSourceProvider();
42
+ this.repository = await provider.getRepository(entity);
43
+ this.repositoryInitialized = true;
44
+ }
45
+ }
46
+ async resolveRepositories(entities) {
47
+ const provider = this.getDataSourceProvider();
48
+ const repositories = [];
49
+ for (const entity of entities){
50
+ repositories.push(await provider.getRepository(entity));
51
+ }
52
+ return repositories;
53
+ }
54
+ async getDataSourceForService() {
55
+ return this.getDataSourceProvider().getDataSource();
56
+ }
18
57
  async insert(dto, user) {
19
- await this.ensureRepositoryInitialized();
20
- const qr = this.repository.manager.connection.createQueryRunner();
21
- await qr.connect();
22
- await qr.startTransaction();
23
- try {
58
+ return this.executeInTransaction('insert', async (qr)=>{
24
59
  await this.beforeInsertOperation(dto, user, qr);
25
60
  const entities = await this.convertRequestDtoToEntity(dto, user);
26
61
  const saved = await qr.manager.save(this.repository.target, entities);
27
- await this.afterInsertOperation(saved, user, qr);
28
- await qr.commitTransaction();
29
- if (this.isCacheable) await Promise.all([
30
- this.clearCacheForAll(),
31
- this.clearCacheForId(Array.isArray(saved) ? saved : [
32
- saved
33
- ])
34
- ]);
35
- const first = Array.isArray(saved) ? saved[0] : saved;
36
- return this.convertEntityToResponseDto(first, false);
37
- } catch (error) {
38
- await qr.rollbackTransaction();
39
- this.handleError(error, 'insert');
40
- } finally{
41
- await qr.release();
42
- }
62
+ await this.afterInsertOperation(this.ensureArray(saved), dto, user, qr);
63
+ return {
64
+ saved,
65
+ returnFirst: true
66
+ };
67
+ });
43
68
  }
44
69
  async insertMany(dtos, user) {
45
- await this.ensureRepositoryInitialized();
46
- const qr = this.repository.manager.connection.createQueryRunner();
47
- await qr.connect();
48
- await qr.startTransaction();
49
- try {
70
+ return this.executeInTransaction('insertMany', async (qr)=>{
50
71
  await this.beforeInsertOperation(dtos, user, qr);
51
72
  const entities = await this.convertRequestDtoToEntity(dtos, user);
52
73
  const saved = await qr.manager.save(this.repository.target, entities);
53
- await this.afterInsertOperation(Array.isArray(saved) ? saved : [
54
- saved
55
- ], user, qr);
56
- await qr.commitTransaction();
57
- if (this.isCacheable) await Promise.all([
58
- this.clearCacheForAll(),
59
- this.clearCacheForId(Array.isArray(saved) ? saved : [
60
- saved
61
- ])
62
- ]);
63
- const list = (Array.isArray(saved) ? saved : [
64
- saved
65
- ]).map((e)=>this.convertEntityToResponseDto(e, false));
66
- return list;
67
- } catch (error) {
68
- await qr.rollbackTransaction();
69
- this.handleError(error, 'insertMany');
70
- } finally{
71
- await qr.release();
72
- }
74
+ await this.afterInsertOperation(this.ensureArray(saved), dtos, user, qr);
75
+ return {
76
+ saved,
77
+ returnFirst: false
78
+ };
79
+ });
73
80
  }
74
81
  async update(dto, user) {
75
- await this.ensureRepositoryInitialized();
76
- const qr = this.repository.manager.connection.createQueryRunner();
77
- await qr.connect();
78
- await qr.startTransaction();
79
- try {
82
+ return this.executeInTransaction('update', async (qr)=>{
80
83
  await this.beforeUpdateOperation(dto, user, qr);
81
84
  const entities = await this.convertRequestDtoToEntity(dto, user);
82
85
  const saved = await qr.manager.save(this.repository.target, entities);
83
- await this.afterUpdateOperation(saved, user, qr);
84
- await qr.commitTransaction();
85
- if (this.isCacheable) await Promise.all([
86
- this.clearCacheForAll(),
87
- this.clearCacheForId(Array.isArray(saved) ? saved : [
88
- saved
89
- ])
90
- ]);
91
- const first = Array.isArray(saved) ? saved[0] : saved;
92
- return this.convertEntityToResponseDto(first, false);
93
- } catch (error) {
94
- await qr.rollbackTransaction();
95
- this.handleError(error, 'update');
96
- } finally{
97
- await qr.release();
98
- }
86
+ await this.afterUpdateOperation(this.ensureArray(saved), dto, user, qr);
87
+ return {
88
+ saved,
89
+ returnFirst: true
90
+ };
91
+ });
99
92
  }
100
93
  async updateMany(dtos, user) {
101
- await this.ensureRepositoryInitialized();
102
- const qr = this.repository.manager.connection.createQueryRunner();
103
- await qr.connect();
104
- await qr.startTransaction();
105
- try {
94
+ return this.executeInTransaction('updateMany', async (qr)=>{
106
95
  await this.beforeUpdateOperation(dtos, user, qr);
107
96
  const entities = await this.convertRequestDtoToEntity(dtos, user);
108
97
  const saved = await qr.manager.save(this.repository.target, entities);
109
- await this.afterUpdateOperation(Array.isArray(saved) ? saved : [
110
- saved
111
- ], user, qr);
112
- await qr.commitTransaction();
113
- if (this.isCacheable) await Promise.all([
114
- this.clearCacheForAll(),
115
- this.clearCacheForId(Array.isArray(saved) ? saved : [
116
- saved
117
- ])
118
- ]);
119
- const list = (Array.isArray(saved) ? saved : [
120
- saved
121
- ]).map((e)=>this.convertEntityToResponseDto(e, false));
122
- return list;
123
- } catch (error) {
124
- await qr.rollbackTransaction();
125
- this.handleError(error, 'updateMany');
126
- } finally{
127
- await qr.release();
128
- }
98
+ await this.afterUpdateOperation(this.ensureArray(saved), dtos, user, qr);
99
+ return {
100
+ saved,
101
+ returnFirst: false
102
+ };
103
+ });
104
+ }
105
+ async bulkUpsert(toInsert, toUpdate, user) {
106
+ return this.executeInTransaction('bulkUpsert', async (qr)=>{
107
+ const allResults = [];
108
+ if (toInsert.length > 0) {
109
+ await this.beforeInsertOperation(toInsert, user, qr);
110
+ const insertedEntities = await this.convertRequestDtoToEntity(toInsert, user);
111
+ const insertedSaved = await qr.manager.save(this.repository.target, insertedEntities);
112
+ await this.afterInsertOperation(this.ensureArray(insertedSaved), toInsert, user, qr);
113
+ allResults.push(...this.ensureArray(insertedSaved));
114
+ }
115
+ if (toUpdate.length > 0) {
116
+ await this.beforeUpdateOperation(toUpdate, user, qr);
117
+ const updatedEntities = await this.convertRequestDtoToEntity(toUpdate, user);
118
+ const updatedSaved = await qr.manager.save(this.repository.target, updatedEntities);
119
+ await this.afterUpdateOperation(this.ensureArray(updatedSaved), toUpdate, user, qr);
120
+ allResults.push(...this.ensureArray(updatedSaved));
121
+ }
122
+ return {
123
+ saved: allResults,
124
+ returnFirst: false
125
+ };
126
+ });
129
127
  }
130
- async findByIds(ids, user) {
131
- await this.ensureRepositoryInitialized();
128
+ async findByIds(ids, user, select) {
129
+ await this.ensureDataSourceRepository();
132
130
  try {
133
131
  const query = this.repository.createQueryBuilder(this.entityName);
134
132
  query.where({
135
133
  id: In(ids)
136
134
  });
137
- const { query: finalQuery, isRaw } = await this.getSelectQuery(query, user);
135
+ const selectArray = typeof select === 'string' ? select.split(',').map((s)=>s.trim()) : select;
136
+ const { query: finalQuery, isRaw } = await this.getSelectQuery(query, user, selectArray);
138
137
  let output;
139
138
  if (isRaw) {
140
139
  output = await finalQuery.getRawMany();
@@ -142,7 +141,10 @@ import { In } from 'typeorm';
142
141
  output = await finalQuery.getMany();
143
142
  }
144
143
  const result = this.convertEntityListToResponseListDto(output, isRaw);
145
- if (!result || !result.length) throw new NotFoundException('No Data Found');
144
+ if (!result || !result.length) throw new NotFoundException({
145
+ message: 'No Data Found',
146
+ messageKey: ERROR_MESSAGES.NOT_FOUND
147
+ });
146
148
  return result;
147
149
  } catch (error) {
148
150
  if (error instanceof NotFoundException) {
@@ -152,7 +154,7 @@ import { In } from 'typeorm';
152
154
  }
153
155
  }
154
156
  async findById(id, user, select) {
155
- await this.ensureRepositoryInitialized();
157
+ await this.ensureDataSourceRepository();
156
158
  try {
157
159
  const query = this.repository.createQueryBuilder(this.entityName);
158
160
  query.where({
@@ -166,15 +168,17 @@ import { In } from 'typeorm';
166
168
  result = await this.cacheManager.get(cacheKey);
167
169
  }
168
170
  if (!result) {
169
- let output;
170
171
  if (isRaw) {
171
- output = await finalQuery.getRawMany();
172
- const convertResult = this.convertEntityListToResponseListDto(output, isRaw);
172
+ const rawOutput = await finalQuery.getRawMany();
173
+ const convertResult = this.convertEntityListToResponseListDto(rawOutput, isRaw);
173
174
  if (convertResult.length) result = convertResult[0];
174
175
  } else {
175
- output = await finalQuery.getOne();
176
- if (!output) throw new NotFoundException('No Data Found');
177
- result = this.convertEntityToResponseDto(output, false);
176
+ const singleOutput = await finalQuery.getOne();
177
+ if (!singleOutput) throw new NotFoundException({
178
+ message: 'No Data Found',
179
+ messageKey: ERROR_MESSAGES.NOT_FOUND
180
+ });
181
+ result = this.convertEntityToResponseDto(singleOutput, false);
178
182
  }
179
183
  if (this.isCacheable) {
180
184
  await this.cacheManager.set(cacheKey, result);
@@ -190,43 +194,34 @@ import { In } from 'typeorm';
190
194
  }
191
195
  }
192
196
  async getAll(search, filterAndPaginationDto, user) {
193
- await this.ensureRepositoryInitialized();
197
+ await this.ensureDataSourceRepository();
194
198
  try {
195
199
  let data;
196
200
  let total;
197
201
  let isRaw = false;
198
202
  let query = this.repository.createQueryBuilder(this.entityName);
199
- // Apply filters
200
203
  if (filterAndPaginationDto.filter) {
201
204
  const { query: filterQuery, isRaw: filterIsRaw } = await this.getFilterQuery(query, filterAndPaginationDto.filter, user);
202
205
  query = filterQuery;
203
206
  isRaw = isRaw || filterIsRaw;
204
207
  }
205
- // Apply search
206
208
  if (search) {
207
209
  const { query: searchQuery, isRaw: searchIsRaw } = await this.getGlobalSearchQuery(query, search, user);
208
210
  query = searchQuery;
209
211
  isRaw = isRaw || searchIsRaw;
210
212
  }
211
- // Apply select
212
- if (filterAndPaginationDto.select) {
213
- const { query: selectQuery, isRaw: selectIsRaw } = await this.getSelectQuery(query, user, filterAndPaginationDto.select);
214
- query = selectQuery;
215
- isRaw = isRaw || selectIsRaw;
216
- }
217
- // Apply sort
213
+ const { query: selectQuery, isRaw: selectIsRaw } = await this.getSelectQuery(query, user, filterAndPaginationDto.select);
214
+ query = selectQuery;
215
+ isRaw = isRaw || selectIsRaw;
218
216
  if (filterAndPaginationDto.sort) {
219
217
  const { query: sortQuery, isRaw: sortIsRaw } = await this.getSortQuery(query, filterAndPaginationDto.sort, user);
220
218
  query = sortQuery;
221
219
  isRaw = isRaw || sortIsRaw;
222
220
  }
223
- // Include soft-deleted if requested
224
221
  if (filterAndPaginationDto.withDeleted) query.withDeleted();
225
- // Extra manipulation
226
222
  const { query: extraQuery, isRaw: extraIsRaw } = await this.getExtraManipulateQuery(query, filterAndPaginationDto, user);
227
223
  query = extraQuery;
228
224
  isRaw = isRaw || extraIsRaw;
229
- // Pagination
230
225
  if (filterAndPaginationDto.pagination) {
231
226
  const offset = filterAndPaginationDto.pagination.currentPage * filterAndPaginationDto.pagination.pageSize;
232
227
  const limit = filterAndPaginationDto.pagination.pageSize;
@@ -236,7 +231,6 @@ import { In } from 'typeorm';
236
231
  query.skip(offset).take(limit);
237
232
  }
238
233
  }
239
- // Generate cache key
240
234
  let cacheKey = '';
241
235
  if (this.isCacheable) {
242
236
  cacheKey = this.utilsService.getCacheKey(this.entityName, {
@@ -250,8 +244,9 @@ import { In } from 'typeorm';
250
244
  }
251
245
  }
252
246
  if (isRaw) {
253
- // Handle raw query count manually
254
- const rawSubQuery = query.clone().offset(undefined).limit(undefined).distinct(true);
247
+ const rawSubQuery = query.clone().offset(undefined).limit(undefined);
248
+ rawSubQuery.expressionMap.orderBys = {};
249
+ rawSubQuery.distinct(true);
255
250
  const countSql = `
256
251
  SELECT COUNT(*) as count
257
252
  FROM (
@@ -262,10 +257,20 @@ import { In } from 'typeorm';
262
257
  `;
263
258
  const parameters = rawSubQuery.getParameters();
264
259
  const orderedValues = [];
260
+ const isPostgres = this.repository.manager.connection.options.type === 'postgres';
261
+ let paramIndex = 0;
265
262
  const convertedQuery = countSql.replace(/:(\w+)/g, (_, key)=>{
266
- if (!(key in parameters)) throw new Error(`Missing parameter value for: ${key}`);
263
+ if (!(key in parameters)) {
264
+ throw new InternalServerErrorException({
265
+ message: `Missing parameter value for: ${key}`,
266
+ messageKey: SYSTEM_MESSAGES.MISSING_PARAMETER,
267
+ messageVariables: {
268
+ key
269
+ }
270
+ });
271
+ }
267
272
  orderedValues.push(parameters[key]);
268
- return '?';
273
+ return isPostgres ? `$${++paramIndex}` : '?';
269
274
  });
270
275
  const result = await this.repository.query(convertedQuery, orderedValues);
271
276
  total = Number(result[0]?.count || 0);
@@ -288,7 +293,7 @@ import { In } from 'typeorm';
288
293
  }
289
294
  }
290
295
  async delete(option, user) {
291
- await this.ensureRepositoryInitialized();
296
+ await this.ensureDataSourceRepository();
292
297
  const queryRunner = this.repository.manager.connection.createQueryRunner();
293
298
  await queryRunner.connect();
294
299
  await queryRunner.startTransaction();
@@ -314,13 +319,12 @@ import { In } from 'typeorm';
314
319
  id
315
320
  };
316
321
  });
317
- await this.afterDeleteOperation(ids, user, queryRunner);
322
+ await this.afterDeleteOperation(ids, option, user, queryRunner);
318
323
  await queryRunner.commitTransaction();
319
324
  await Promise.all([
320
325
  this.clearCacheForAll(),
321
326
  this.clearCacheForId(ids)
322
327
  ]);
323
- // return null per IService contract
324
328
  return null;
325
329
  } catch (error) {
326
330
  await queryRunner.rollbackTransaction();
@@ -329,29 +333,58 @@ import { In } from 'typeorm';
329
333
  await queryRunner.release();
330
334
  }
331
335
  }
332
- // Caching
333
336
  async clearCacheForAll() {
334
337
  await this.utilsService.clearCache(this.entityName, this.cacheManager);
335
338
  }
336
339
  async clearCacheForId(entities) {
337
340
  await Promise.all(entities.map((item)=>this.utilsService.clearCache(this.entityName, this.cacheManager, item.id)));
338
341
  }
339
- /**
340
- * Helper method to handle errors with proper logging and type safety
341
- */ handleError(error, operation) {
342
- ErrorHandler.logError(this.logger, error, operation, {
343
- entity: this.entityName
342
+ handleError(error, _operation) {
343
+ if (error instanceof Error) {
344
+ throw error;
345
+ }
346
+ throw new InternalServerErrorException({
347
+ message: typeof error === 'string' ? error : 'Unknown error',
348
+ messageKey: ERROR_MESSAGES.INTERNAL
344
349
  });
345
- ErrorHandler.rethrowError(error);
346
350
  }
347
- // Hooks (override in child classes)
348
- async ensureRepositoryInitialized() {}
351
+ ensureArray(value) {
352
+ return Array.isArray(value) ? value : [
353
+ value
354
+ ];
355
+ }
356
+ async executeInTransaction(operation, fn) {
357
+ await this.ensureDataSourceRepository();
358
+ const qr = this.repository.manager.connection.createQueryRunner();
359
+ await qr.connect();
360
+ await qr.startTransaction();
361
+ try {
362
+ const { saved, returnFirst } = await fn(qr);
363
+ await qr.commitTransaction();
364
+ const savedArray = this.ensureArray(saved);
365
+ if (this.isCacheable) {
366
+ await Promise.all([
367
+ this.clearCacheForAll(),
368
+ this.clearCacheForId(savedArray)
369
+ ]);
370
+ }
371
+ if (returnFirst) {
372
+ return this.convertEntityToResponseDto(savedArray[0], false);
373
+ }
374
+ return savedArray.map((e)=>this.convertEntityToResponseDto(e, false));
375
+ } catch (error) {
376
+ await qr.rollbackTransaction();
377
+ this.handleError(error, operation);
378
+ } finally{
379
+ await qr.release();
380
+ }
381
+ }
349
382
  async beforeInsertOperation(_dto, _user, _queryRunner) {}
350
- async afterInsertOperation(_entity, _user, _queryRunner) {}
383
+ async afterInsertOperation(_entity, _dto, _user, _queryRunner) {}
351
384
  async beforeUpdateOperation(_dto, _user, _queryRunner) {}
352
- async afterUpdateOperation(_entity, _user, _queryRunner) {}
385
+ async afterUpdateOperation(_entity, _dto, _user, _queryRunner) {}
353
386
  async beforeDeleteOperation(_dto, _user, _queryRunner) {}
354
- async afterDeleteOperation(_entity, _user, _queryRunner) {}
387
+ async afterDeleteOperation(_entity, _dto, _user, _queryRunner) {}
355
388
  async getFilterQuery(query, filter, _user) {
356
389
  Object.entries(filter).forEach(([key, value])=>{
357
390
  query.andWhere(`${this.entityName}.${key} = :value`, {
@@ -394,7 +427,6 @@ import { In } from 'typeorm';
394
427
  isRaw: false
395
428
  };
396
429
  }
397
- // DTO conversion
398
430
  async convertRequestDtoToEntity(dto, user) {
399
431
  return Array.isArray(dto) ? await this.convertArrayDtoToEntities(dto, user) : [
400
432
  await this.convertSingleDtoToEntity(dto, user)
@@ -408,7 +440,10 @@ import { In } from 'typeorm';
408
440
  id: d.id
409
441
  }
410
442
  });
411
- if (!existing) throw new NotFoundException('No such entity data found for update! Please, Try Again.');
443
+ if (!existing) throw new NotFoundException({
444
+ message: 'No such entity data found for update! Please, Try Again.',
445
+ messageKey: ERROR_MESSAGES.NOT_FOUND
446
+ });
412
447
  return Object.assign(existing, d);
413
448
  }
414
449
  delete d.id;
@@ -421,26 +456,113 @@ import { In } from 'typeorm';
421
456
  return Object.assign({}, entity);
422
457
  }
423
458
  convertEntityListToResponseListDto(entities, _isRaw) {
424
- return entities.map((entity)=>Object.assign({}, entity));
459
+ return entities.map((entity)=>this.convertEntityToResponseDto(entity, _isRaw));
425
460
  }
426
461
  async getEntityClass() {
427
- return await this.repository.create();
462
+ return this.repository.create();
428
463
  }
429
- constructor(entityName, repository, cacheManager, utilsService, loggerName, isCacheable = false){
464
+ constructor(entityName, cacheManager, utilsService, _loggerName, isCacheable = false, moduleName, entity, dataSourceProvider){
430
465
  _define_property(this, "entityName", void 0);
431
- _define_property(this, "repository", void 0);
432
466
  _define_property(this, "cacheManager", void 0);
433
467
  _define_property(this, "utilsService", void 0);
434
- _define_property(this, "loggerName", void 0);
468
+ _define_property(this, "_loggerName", void 0);
435
469
  _define_property(this, "isCacheable", void 0);
436
- _define_property(this, "logger", void 0);
470
+ _define_property(this, "moduleName", void 0);
471
+ _define_property(this, "repository", void 0);
472
+ _define_property(this, "repositoryInitialized", void 0);
473
+ _define_property(this, "_entity", void 0);
474
+ _define_property(this, "_dataSourceProvider", void 0);
437
475
  this.entityName = entityName;
438
- this.repository = repository;
439
476
  this.cacheManager = cacheManager;
440
477
  this.utilsService = utilsService;
441
- this.loggerName = loggerName;
478
+ this._loggerName = _loggerName;
442
479
  this.isCacheable = isCacheable;
443
- this.logger = new Logger('ApiService');
444
- this.logger = new Logger(loggerName);
480
+ this.moduleName = moduleName;
481
+ this.repositoryInitialized = false;
482
+ this._entity = entity;
483
+ this._dataSourceProvider = dataSourceProvider;
445
484
  }
446
485
  }
486
+ _ts_decorate([
487
+ LogAction({
488
+ action: 'create'
489
+ }),
490
+ _ts_metadata("design:type", Function),
491
+ _ts_metadata("design:paramtypes", [
492
+ typeof CreateDtoT === "undefined" ? Object : CreateDtoT,
493
+ Object
494
+ ]),
495
+ _ts_metadata("design:returntype", Promise)
496
+ ], ApiService.prototype, "insert", null);
497
+ _ts_decorate([
498
+ LogAction({
499
+ action: 'createMany'
500
+ }),
501
+ _ts_metadata("design:type", Function),
502
+ _ts_metadata("design:paramtypes", [
503
+ typeof Array === "undefined" ? Object : Array,
504
+ Object
505
+ ]),
506
+ _ts_metadata("design:returntype", Promise)
507
+ ], ApiService.prototype, "insertMany", null);
508
+ _ts_decorate([
509
+ LogAction({
510
+ action: 'update'
511
+ }),
512
+ _ts_metadata("design:type", Function),
513
+ _ts_metadata("design:paramtypes", [
514
+ typeof UpdateDtoT === "undefined" ? Object : UpdateDtoT,
515
+ Object
516
+ ]),
517
+ _ts_metadata("design:returntype", Promise)
518
+ ], ApiService.prototype, "update", null);
519
+ _ts_decorate([
520
+ LogAction({
521
+ action: 'updateMany'
522
+ }),
523
+ _ts_metadata("design:type", Function),
524
+ _ts_metadata("design:paramtypes", [
525
+ Array,
526
+ Object
527
+ ]),
528
+ _ts_metadata("design:returntype", Promise)
529
+ ], ApiService.prototype, "updateMany", null);
530
+ _ts_decorate([
531
+ LogAction({
532
+ action: 'bulkUpsert'
533
+ }),
534
+ _ts_metadata("design:type", Function),
535
+ _ts_metadata("design:paramtypes", [
536
+ Array,
537
+ Array,
538
+ Object
539
+ ]),
540
+ _ts_metadata("design:returntype", Promise)
541
+ ], ApiService.prototype, "bulkUpsert", null);
542
+ _ts_decorate([
543
+ LogAction({
544
+ action: 'delete'
545
+ }),
546
+ _ts_metadata("design:type", Function),
547
+ _ts_metadata("design:paramtypes", [
548
+ typeof DeleteDto === "undefined" ? Object : DeleteDto,
549
+ Object
550
+ ]),
551
+ _ts_metadata("design:returntype", Promise)
552
+ ], ApiService.prototype, "delete", null);
553
+ ApiService = _ts_decorate([
554
+ Injectable({
555
+ scope: Scope.REQUEST
556
+ }),
557
+ _ts_metadata("design:type", Function),
558
+ _ts_metadata("design:paramtypes", [
559
+ String,
560
+ typeof HybridCache === "undefined" ? Object : HybridCache,
561
+ typeof UtilsService === "undefined" ? Object : UtilsService,
562
+ String,
563
+ Boolean,
564
+ String,
565
+ typeof EntityTarget === "undefined" ? Object : EntityTarget,
566
+ typeof IDataSourceProvider === "undefined" ? Object : IDataSourceProvider
567
+ ])
568
+ ], ApiService);
@@ -0,0 +1,19 @@
1
+ import { envConfig } from '@flusys/nestjs-core';
2
+ export function getJsonColumnType() {
3
+ const driver = envConfig.getTypeOrmConfig().type;
4
+ if (driver === 'postgres') return 'jsonb';
5
+ if (driver === 'mysql') return 'json';
6
+ return 'simple-json';
7
+ }
8
+ export function getCastIntType() {
9
+ const driver = envConfig.getTypeOrmConfig().type;
10
+ return driver === 'postgres' ? 'INTEGER' : 'UNSIGNED';
11
+ }
12
+ /**
13
+ * Converts MySQL-style `?` placeholders to PostgreSQL `$1, $2, ...` placeholders.
14
+ * No-op for MySQL/MariaDB.
15
+ */ export function buildQuery(sql, dbType) {
16
+ if (dbType !== 'postgres') return sql;
17
+ let index = 0;
18
+ return sql.replace(/\?/g, ()=>`$${++index}`);
19
+ }
@@ -1,6 +1,7 @@
1
1
  export * from './api-controller.class';
2
2
  export * from './api-service.class';
3
- export * from './request-scoped-api.service';
3
+ export * from './handle-custome-db';
4
4
  export * from './hybrid-cache.class';
5
- export * from './winston-logger-adapter.class';
6
5
  export * from './winston.logger.class';
6
+ // Re-export permission constants for convenience
7
+ export * from '../constants/permissions';