@flusys/nestjs-auth 7.0.2 → 8.0.0-beta.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 (237) hide show
  1. package/README.md +68 -1
  2. package/config/auth.constants.d.ts +2 -0
  3. package/config/event-actions.d.ts +2 -0
  4. package/config/index.d.ts +3 -3
  5. package/config/message-keys.d.ts +23 -0
  6. package/controllers/authentication.controller.d.ts +5 -5
  7. package/controllers/branch.controller.d.ts +2 -2
  8. package/controllers/company-selection.controller.d.ts +4 -4
  9. package/controllers/company.controller.d.ts +2 -2
  10. package/controllers/email-verification.controller.d.ts +6 -6
  11. package/controllers/index.d.ts +9 -7
  12. package/controllers/social-auth-config.controller.d.ts +22 -0
  13. package/controllers/social-auth.controller.d.ts +11 -0
  14. package/controllers/user-permission.controller.d.ts +5 -4
  15. package/controllers/user.controller.d.ts +7 -7
  16. package/docs/auth-swagger.config.d.ts +2 -2
  17. package/docs/index.d.ts +1 -1
  18. package/dtos/index.d.ts +6 -5
  19. package/dtos/social-auth.dto.d.ts +42 -0
  20. package/entities/company-branch.entity.d.ts +1 -1
  21. package/entities/company.entity.d.ts +1 -1
  22. package/entities/index.d.ts +15 -13
  23. package/entities/pending-registration.entity.d.ts +1 -1
  24. package/entities/social-auth-config.entity.d.ts +9 -0
  25. package/entities/user-company-permission.entity.d.ts +4 -4
  26. package/enums/index.d.ts +2 -1
  27. package/enums/social-provider-type.enum.d.ts +6 -0
  28. package/fesm/118.js +1230 -0
  29. package/fesm/120.js +2197 -0
  30. package/fesm/24.js +4389 -0
  31. package/fesm/370.js +1163 -0
  32. package/fesm/446.js +877 -0
  33. package/fesm/chunks/117.js +69 -0
  34. package/fesm/config/index.js +244 -3
  35. package/fesm/controllers/index.js +27 -7
  36. package/fesm/docs/index.js +479 -1
  37. package/fesm/dtos/index.js +123 -5
  38. package/fesm/entities/index.js +68 -19
  39. package/fesm/enums/index.js +53 -1
  40. package/fesm/helpers/index.js +115 -2
  41. package/fesm/index.js +1437 -12
  42. package/fesm/interceptors/index.js +508 -2
  43. package/fesm/interfaces/index.js +75 -7
  44. package/fesm/modules/index.js +546 -1
  45. package/fesm/runtime.js +134 -0
  46. package/fesm/services/index.js +67 -9
  47. package/helpers/auth-response.mapper.d.ts +3 -3
  48. package/helpers/index.d.ts +2 -2
  49. package/helpers/token.helper.d.ts +2 -2
  50. package/index.d.ts +13 -12
  51. package/interceptors/clear-token.interceptor.d.ts +2 -2
  52. package/interceptors/index.d.ts +2 -2
  53. package/interceptors/set-token.interceptor.d.ts +2 -2
  54. package/interfaces/auth-module-options.interface.d.ts +3 -3
  55. package/interfaces/authentication.interface.d.ts +3 -3
  56. package/interfaces/company-branch.interface.d.ts +2 -2
  57. package/interfaces/company.interface.d.ts +1 -1
  58. package/interfaces/index.d.ts +8 -7
  59. package/interfaces/social-auth.interface.d.ts +43 -0
  60. package/interfaces/user-enricher.interface.d.ts +3 -3
  61. package/modules/auth.module.d.ts +4 -4
  62. package/modules/index.d.ts +1 -1
  63. package/package.json +32 -39
  64. package/providers/facebook.social-provider.d.ts +10 -0
  65. package/providers/google.social-provider.d.ts +10 -0
  66. package/providers/index.d.ts +1 -0
  67. package/providers/linkedin.social-provider.d.ts +10 -0
  68. package/providers/microsoft.social-provider.d.ts +10 -0
  69. package/providers/oauth-http.util.d.ts +10 -0
  70. package/providers/social-provider.registry.d.ts +3 -0
  71. package/services/auth-config.service.d.ts +4 -3
  72. package/services/auth-datasource.provider.d.ts +4 -3
  73. package/services/auth-email.service.d.ts +3 -3
  74. package/services/authentication.service.d.ts +15 -11
  75. package/services/branch.service.d.ts +7 -7
  76. package/services/company.service.d.ts +6 -6
  77. package/services/index.d.ts +11 -9
  78. package/services/social-auth-config.service.d.ts +21 -0
  79. package/services/social-auth.service.d.ts +24 -0
  80. package/services/user-permission.service.d.ts +8 -4
  81. package/services/user.service.d.ts +7 -7
  82. package/strategies/jwt.strategy.d.ts +4 -4
  83. package/validators/password.validator.d.ts +1 -1
  84. package/cjs/config/auth.constants.js +0 -41
  85. package/cjs/config/event-actions.js +0 -45
  86. package/cjs/config/index.js +0 -20
  87. package/cjs/config/message-keys.js +0 -108
  88. package/cjs/controllers/authentication.controller.js +0 -427
  89. package/cjs/controllers/authentication.controller.spec.js +0 -219
  90. package/cjs/controllers/branch.controller.js +0 -102
  91. package/cjs/controllers/branch.controller.spec.js +0 -134
  92. package/cjs/controllers/company-selection.controller.js +0 -198
  93. package/cjs/controllers/company-selection.controller.spec.js +0 -84
  94. package/cjs/controllers/company.controller.js +0 -102
  95. package/cjs/controllers/company.controller.spec.js +0 -131
  96. package/cjs/controllers/email-verification.controller.js +0 -224
  97. package/cjs/controllers/email-verification.controller.spec.js +0 -142
  98. package/cjs/controllers/index.js +0 -24
  99. package/cjs/controllers/user-permission.controller.js +0 -223
  100. package/cjs/controllers/user-permission.controller.spec.js +0 -138
  101. package/cjs/controllers/user.controller.js +0 -438
  102. package/cjs/controllers/user.controller.spec.js +0 -204
  103. package/cjs/docs/auth-swagger.config.js +0 -453
  104. package/cjs/docs/auth-swagger.config.spec.js +0 -130
  105. package/cjs/docs/index.js +0 -18
  106. package/cjs/dtos/authentication.dto.js +0 -755
  107. package/cjs/dtos/company-branch.dto.js +0 -294
  108. package/cjs/dtos/company.dto.js +0 -298
  109. package/cjs/dtos/index.js +0 -22
  110. package/cjs/dtos/user-permission.dto.js +0 -358
  111. package/cjs/dtos/user.dto.js +0 -413
  112. package/cjs/entities/app-user.entity.js +0 -25
  113. package/cjs/entities/company-branch.entity.js +0 -163
  114. package/cjs/entities/company.entity.js +0 -146
  115. package/cjs/entities/index.js +0 -65
  116. package/cjs/entities/index.spec.js +0 -73
  117. package/cjs/entities/pending-registration.entity.js +0 -135
  118. package/cjs/entities/user-company-permission.entity.js +0 -153
  119. package/cjs/entities/user-token.entity.js +0 -120
  120. package/cjs/enums/index.js +0 -18
  121. package/cjs/enums/user-org-permission-type.enum.js +0 -15
  122. package/cjs/helpers/auth-response.mapper.js +0 -46
  123. package/cjs/helpers/auth-response.mapper.spec.js +0 -139
  124. package/cjs/helpers/index.js +0 -19
  125. package/cjs/helpers/token.helper.js +0 -58
  126. package/cjs/helpers/token.helper.spec.js +0 -86
  127. package/cjs/index.js +0 -29
  128. package/cjs/interceptors/clear-token.interceptor.js +0 -73
  129. package/cjs/interceptors/clear-token.interceptor.spec.js +0 -107
  130. package/cjs/interceptors/index.js +0 -19
  131. package/cjs/interceptors/set-token.interceptor.js +0 -107
  132. package/cjs/interceptors/set-token.interceptor.spec.js +0 -165
  133. package/cjs/interfaces/auth-email-provider.interface.js +0 -45
  134. package/cjs/interfaces/auth-module-options.interface.js +0 -4
  135. package/cjs/interfaces/authentication.interface.js +0 -4
  136. package/cjs/interfaces/company-branch.interface.js +0 -4
  137. package/cjs/interfaces/company.interface.js +0 -4
  138. package/cjs/interfaces/index.js +0 -24
  139. package/cjs/interfaces/user-enricher.interface.js +0 -4
  140. package/cjs/interfaces/user.interface.js +0 -4
  141. package/cjs/modules/auth.module.js +0 -202
  142. package/cjs/modules/auth.module.spec.js +0 -243
  143. package/cjs/modules/index.js +0 -18
  144. package/cjs/services/auth-config.service.js +0 -110
  145. package/cjs/services/auth-config.service.spec.js +0 -97
  146. package/cjs/services/auth-datasource.provider.js +0 -134
  147. package/cjs/services/auth-datasource.provider.spec.js +0 -161
  148. package/cjs/services/auth-email.service.js +0 -539
  149. package/cjs/services/auth-email.service.spec.js +0 -456
  150. package/cjs/services/authentication.service.js +0 -1173
  151. package/cjs/services/authentication.service.spec.js +0 -962
  152. package/cjs/services/branch.service.js +0 -153
  153. package/cjs/services/branch.service.spec.js +0 -169
  154. package/cjs/services/company-selection-session.service.js +0 -121
  155. package/cjs/services/company-selection-session.service.spec.js +0 -90
  156. package/cjs/services/company.service.js +0 -140
  157. package/cjs/services/company.service.spec.js +0 -127
  158. package/cjs/services/index.js +0 -26
  159. package/cjs/services/user-permission.service.js +0 -236
  160. package/cjs/services/user-permission.service.spec.js +0 -254
  161. package/cjs/services/user.service.js +0 -671
  162. package/cjs/services/user.service.spec.js +0 -718
  163. package/cjs/strategies/jwt.strategy.js +0 -140
  164. package/cjs/strategies/jwt.strategy.spec.js +0 -166
  165. package/cjs/validators/password.validator.js +0 -45
  166. package/cjs/validators/password.validator.spec.js +0 -161
  167. package/fesm/config/auth.constants.js +0 -11
  168. package/fesm/config/event-actions.js +0 -24
  169. package/fesm/config/message-keys.js +0 -78
  170. package/fesm/controllers/authentication.controller.js +0 -417
  171. package/fesm/controllers/authentication.controller.spec.js +0 -215
  172. package/fesm/controllers/branch.controller.js +0 -92
  173. package/fesm/controllers/branch.controller.spec.js +0 -130
  174. package/fesm/controllers/company-selection.controller.js +0 -188
  175. package/fesm/controllers/company-selection.controller.spec.js +0 -80
  176. package/fesm/controllers/company.controller.js +0 -92
  177. package/fesm/controllers/company.controller.spec.js +0 -127
  178. package/fesm/controllers/email-verification.controller.js +0 -214
  179. package/fesm/controllers/email-verification.controller.spec.js +0 -138
  180. package/fesm/controllers/user-permission.controller.js +0 -213
  181. package/fesm/controllers/user-permission.controller.spec.js +0 -134
  182. package/fesm/controllers/user.controller.js +0 -428
  183. package/fesm/controllers/user.controller.spec.js +0 -200
  184. package/fesm/docs/auth-swagger.config.js +0 -446
  185. package/fesm/docs/auth-swagger.config.spec.js +0 -126
  186. package/fesm/dtos/authentication.dto.js +0 -683
  187. package/fesm/dtos/company-branch.dto.js +0 -279
  188. package/fesm/dtos/company.dto.js +0 -283
  189. package/fesm/dtos/user-permission.dto.js +0 -339
  190. package/fesm/dtos/user.dto.js +0 -391
  191. package/fesm/entities/app-user.entity.js +0 -15
  192. package/fesm/entities/company-branch.entity.js +0 -153
  193. package/fesm/entities/company.entity.js +0 -136
  194. package/fesm/entities/index.spec.js +0 -69
  195. package/fesm/entities/pending-registration.entity.js +0 -125
  196. package/fesm/entities/user-company-permission.entity.js +0 -136
  197. package/fesm/entities/user-token.entity.js +0 -104
  198. package/fesm/enums/user-org-permission-type.enum.js +0 -5
  199. package/fesm/helpers/auth-response.mapper.js +0 -25
  200. package/fesm/helpers/auth-response.mapper.spec.js +0 -135
  201. package/fesm/helpers/token.helper.js +0 -40
  202. package/fesm/helpers/token.helper.spec.js +0 -82
  203. package/fesm/interceptors/clear-token.interceptor.js +0 -63
  204. package/fesm/interceptors/clear-token.interceptor.spec.js +0 -103
  205. package/fesm/interceptors/set-token.interceptor.js +0 -98
  206. package/fesm/interceptors/set-token.interceptor.spec.js +0 -161
  207. package/fesm/interfaces/auth-email-provider.interface.js +0 -38
  208. package/fesm/interfaces/auth-module-options.interface.js +0 -3
  209. package/fesm/interfaces/authentication.interface.js +0 -1
  210. package/fesm/interfaces/company-branch.interface.js +0 -1
  211. package/fesm/interfaces/company.interface.js +0 -1
  212. package/fesm/interfaces/user-enricher.interface.js +0 -4
  213. package/fesm/interfaces/user.interface.js +0 -1
  214. package/fesm/modules/auth.module.js +0 -192
  215. package/fesm/modules/auth.module.spec.js +0 -239
  216. package/fesm/services/auth-config.service.js +0 -100
  217. package/fesm/services/auth-config.service.spec.js +0 -93
  218. package/fesm/services/auth-datasource.provider.js +0 -83
  219. package/fesm/services/auth-datasource.provider.spec.js +0 -157
  220. package/fesm/services/auth-email.service.js +0 -488
  221. package/fesm/services/auth-email.service.spec.js +0 -411
  222. package/fesm/services/authentication.service.js +0 -1122
  223. package/fesm/services/authentication.service.spec.js +0 -917
  224. package/fesm/services/branch.service.js +0 -143
  225. package/fesm/services/branch.service.spec.js +0 -165
  226. package/fesm/services/company-selection-session.service.js +0 -111
  227. package/fesm/services/company-selection-session.service.spec.js +0 -86
  228. package/fesm/services/company.service.js +0 -130
  229. package/fesm/services/company.service.spec.js +0 -123
  230. package/fesm/services/user-permission.service.js +0 -226
  231. package/fesm/services/user-permission.service.spec.js +0 -250
  232. package/fesm/services/user.service.js +0 -620
  233. package/fesm/services/user.service.spec.js +0 -673
  234. package/fesm/strategies/jwt.strategy.js +0 -130
  235. package/fesm/strategies/jwt.strategy.spec.js +0 -162
  236. package/fesm/validators/password.validator.js +0 -48
  237. package/fesm/validators/password.validator.spec.js +0 -157
@@ -1,718 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- const _common = require("@nestjs/common");
6
- const _classes = require("@flusys/nestjs-shared/classes");
7
- const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
8
- const _repositorymock = require("@test-utils/mocks/repository.mock");
9
- const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
10
- const _userservice = require("./user.service");
11
- const _appuserentity = require("../entities/app-user.entity");
12
- const _usercompanypermissionentity = require("../entities/user-company-permission.entity");
13
- function _getRequireWildcardCache(nodeInterop) {
14
- if (typeof WeakMap !== "function") return null;
15
- var cacheBabelInterop = new WeakMap();
16
- var cacheNodeInterop = new WeakMap();
17
- return (_getRequireWildcardCache = function(nodeInterop) {
18
- return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
19
- })(nodeInterop);
20
- }
21
- function _interop_require_wildcard(obj, nodeInterop) {
22
- if (!nodeInterop && obj && obj.__esModule) {
23
- return obj;
24
- }
25
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
26
- return {
27
- default: obj
28
- };
29
- }
30
- var cache = _getRequireWildcardCache(nodeInterop);
31
- if (cache && cache.has(obj)) {
32
- return cache.get(obj);
33
- }
34
- var newObj = {
35
- __proto__: null
36
- };
37
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
38
- for(var key in obj){
39
- if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
40
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
41
- if (desc && (desc.get || desc.set)) {
42
- Object.defineProperty(newObj, key, desc);
43
- } else {
44
- newObj[key] = obj[key];
45
- }
46
- }
47
- }
48
- newObj.default = obj;
49
- if (cache) {
50
- cache.set(obj, newObj);
51
- }
52
- return newObj;
53
- }
54
- jest.mock('bcrypt', ()=>({
55
- hash: jest.fn().mockResolvedValue('hashed-password'),
56
- compare: jest.fn().mockResolvedValue(true)
57
- }));
58
- function buildAppUser(overrides = {}) {
59
- return {
60
- id: 'user-1',
61
- name: 'Test User',
62
- email: 'user@example.com',
63
- phone: null,
64
- password: 'old-hashed',
65
- isActive: true,
66
- emailVerified: false,
67
- phoneVerified: false,
68
- profilePictureId: null,
69
- lastLoginAt: null,
70
- additionalFields: null,
71
- createdAt: new Date(),
72
- updatedAt: new Date(),
73
- deletedAt: null,
74
- createdById: null,
75
- updatedById: null,
76
- deletedById: null,
77
- ...overrides
78
- };
79
- }
80
- describe('UserService', ()=>{
81
- let service;
82
- let userRepo;
83
- let permissionRepo;
84
- let mockUtilsService;
85
- let mockAuthConfig;
86
- let mockDataSourceProvider;
87
- let mockUserEnricher;
88
- let companyFeatureEnabled;
89
- let mockDataSource;
90
- beforeEach(()=>{
91
- jest.clearAllMocks();
92
- _bcrypt.compare.mockResolvedValue(true);
93
- _bcrypt.hash.mockResolvedValue('hashed-password');
94
- userRepo = (0, _repositorymock.createMockRepository)();
95
- permissionRepo = (0, _repositorymock.createMockRepository)();
96
- companyFeatureEnabled = false;
97
- mockUtilsService = {
98
- clearCache: jest.fn()
99
- };
100
- mockAuthConfig = {
101
- isCompanyFeatureEnabled: jest.fn().mockImplementation(()=>companyFeatureEnabled),
102
- isPermissionModeFound: jest.fn().mockReturnValue(false)
103
- };
104
- mockDataSource = {
105
- createQueryRunner: jest.fn()
106
- };
107
- mockDataSourceProvider = {
108
- getRepository: jest.fn().mockImplementation(async (entity)=>{
109
- if (entity === _appuserentity.AppUser) return userRepo;
110
- if (entity === _usercompanypermissionentity.UserCompanyPermission) return permissionRepo;
111
- throw new Error('unexpected entity');
112
- }),
113
- getDataSource: jest.fn().mockResolvedValue(mockDataSource)
114
- };
115
- mockUserEnricher = {
116
- enrichListItems: jest.fn(),
117
- getProfileExtras: jest.fn(),
118
- getProfileSections: jest.fn(),
119
- getProfileSectionData: jest.fn(),
120
- updateProfileSection: jest.fn(),
121
- calculateProfileCompletion: jest.fn(),
122
- validateProfileExtras: jest.fn(),
123
- updateProfileExtras: jest.fn()
124
- };
125
- service = new _userservice.UserService({}, mockUtilsService, mockDataSourceProvider, mockAuthConfig, mockUserEnricher);
126
- });
127
- /**
128
- * Every write path has to drop both the list cache and the row's own cache, or a
129
- * stale user keeps being served from `get-all`.
130
- */ function expectCacheCleared(id) {
131
- expect(mockUtilsService.clearCache).toHaveBeenCalledWith('appuser', expect.anything());
132
- expect(mockUtilsService.clearCache).toHaveBeenCalledWith('appuser', expect.anything(), id);
133
- }
134
- describe('ensureDataSourceRepository', ()=>{
135
- it('does not load the permission repository when the company feature is disabled', async ()=>{
136
- await service.ensureDataSourceRepository();
137
- expect(mockDataSourceProvider.getRepository).toHaveBeenCalledWith(_appuserentity.AppUser);
138
- expect(mockDataSourceProvider.getRepository).not.toHaveBeenCalledWith(_usercompanypermissionentity.UserCompanyPermission);
139
- });
140
- it('loads the permission repository when the company feature is enabled', async ()=>{
141
- companyFeatureEnabled = true;
142
- await service.ensureDataSourceRepository();
143
- expect(mockDataSourceProvider.getRepository).toHaveBeenCalledWith(_usercompanypermissionentity.UserCompanyPermission);
144
- });
145
- });
146
- describe('verifyEmail / verifyPhone', ()=>{
147
- it('marks the email as verified and clears the cache', async ()=>{
148
- await service.verifyEmail('user-1');
149
- expect(userRepo.update).toHaveBeenCalledWith('user-1', {
150
- emailVerified: true
151
- });
152
- expectCacheCleared('user-1');
153
- });
154
- it('marks the phone as verified and clears the cache', async ()=>{
155
- await service.verifyPhone('user-1');
156
- expect(userRepo.update).toHaveBeenCalledWith('user-1', {
157
- phoneVerified: true
158
- });
159
- expectCacheCleared('user-1');
160
- });
161
- });
162
- describe('updateStatus', ()=>{
163
- it('updates the user directly when the company feature is disabled', async ()=>{
164
- await service.updateStatus('user-1', false, null);
165
- expect(userRepo.update).toHaveBeenCalledWith('user-1', {
166
- isActive: false
167
- });
168
- expectCacheCleared('user-1');
169
- });
170
- it('throws NotFoundException when the company permission is missing', async ()=>{
171
- companyFeatureEnabled = true;
172
- permissionRepo.findOne.mockResolvedValue(null);
173
- await expect(service.updateStatus('user-1', false, (0, _loggedusermock.buildMockUser)({
174
- companyId: 'company-1'
175
- }))).rejects.toThrow(_common.NotFoundException);
176
- });
177
- it('updates the company permission status when the company feature is enabled', async ()=>{
178
- companyFeatureEnabled = true;
179
- const permission = {
180
- isActive: true
181
- };
182
- permissionRepo.findOne.mockResolvedValue(permission);
183
- permissionRepo.save.mockResolvedValue(permission);
184
- await service.updateStatus('user-1', false, (0, _loggedusermock.buildMockUser)({
185
- companyId: 'company-1'
186
- }));
187
- expect(permissionRepo.save).toHaveBeenCalledWith(expect.objectContaining({
188
- isActive: false
189
- }));
190
- expect(userRepo.update).not.toHaveBeenCalled();
191
- expectCacheCleared('user-1');
192
- });
193
- });
194
- describe('convertSingleDtoToEntity', ()=>{
195
- it('hashes the password when present in the DTO', async ()=>{
196
- service.repository = userRepo;
197
- userRepo.create.mockReturnValue({
198
- password: null
199
- });
200
- const entity = await service.convertSingleDtoToEntity({
201
- name: 'New User',
202
- email: 'new@example.com',
203
- password: 'Password1!'
204
- }, null);
205
- expect(_bcrypt.hash).toHaveBeenCalledWith('Password1!', 12);
206
- expect(entity.password).toBe('hashed-password');
207
- });
208
- it('does not touch the password field when absent from the DTO', async ()=>{
209
- service.repository = userRepo;
210
- userRepo.create.mockReturnValue({
211
- password: null
212
- });
213
- const entity = await service.convertSingleDtoToEntity({
214
- name: 'New User',
215
- email: 'new@example.com'
216
- }, null);
217
- expect(_bcrypt.hash).not.toHaveBeenCalled();
218
- expect(entity.password).toBeNull();
219
- });
220
- });
221
- describe('convertEntityToResponseDto', ()=>{
222
- it('maps a plain entity when isRaw is false', ()=>{
223
- const user = buildAppUser();
224
- const dto = service.convertEntityToResponseDto(user, false);
225
- expect(dto).toEqual(expect.objectContaining({
226
- id: 'user-1',
227
- email: 'user@example.com'
228
- }));
229
- });
230
- it('maps a raw query result and derives isActive from the company permission when present', ()=>{
231
- const raw = {
232
- appuser_id: 'user-1',
233
- appuser_name: 'Test User',
234
- appuser_phone: null,
235
- appuser_email: 'user@example.com',
236
- appuser_profile_picture_id: null,
237
- appuser_is_active: true,
238
- appuser_companyIsActive: false,
239
- appuser_email_verified: false,
240
- appuser_phone_verified: false,
241
- appuser_last_login_at: null,
242
- appuser_additional_fields: null,
243
- appuser_created_at: new Date(),
244
- appuser_updated_at: new Date(),
245
- appuser_deleted_at: null,
246
- appuser_created_by_id: null,
247
- appuser_updated_by_id: null,
248
- appuser_deleted_by_id: null
249
- };
250
- const dto = service.convertEntityToResponseDto(raw, true);
251
- expect(dto.isActive).toBe(false);
252
- });
253
- it('falls back to the base isActive column when no company permission flag is present in the raw row', ()=>{
254
- const raw = {
255
- appuser_id: 'user-1',
256
- appuser_name: 'Test User',
257
- appuser_phone: null,
258
- appuser_email: 'user@example.com',
259
- appuser_profile_picture_id: null,
260
- appuser_is_active: true,
261
- appuser_email_verified: false,
262
- appuser_phone_verified: false,
263
- appuser_last_login_at: null,
264
- appuser_additional_fields: null,
265
- appuser_created_at: new Date(),
266
- appuser_updated_at: new Date(),
267
- appuser_deleted_at: null,
268
- appuser_created_by_id: null,
269
- appuser_updated_by_id: null,
270
- appuser_deleted_by_id: null
271
- };
272
- const dto = service.convertEntityToResponseDto(raw, true);
273
- expect(dto.isActive).toBe(true);
274
- });
275
- });
276
- describe('getFilterQuery', ()=>{
277
- it('skips actionCode and isActive keys but applies other filters', async ()=>{
278
- const qb = {
279
- andWhere: jest.fn().mockReturnThis()
280
- };
281
- await service.getFilterQuery(qb, {
282
- actionCode: 'x',
283
- isActive: true,
284
- name: 'John'
285
- }, null);
286
- expect(qb.andWhere).toHaveBeenCalledTimes(1);
287
- expect(qb.andWhere).toHaveBeenCalledWith('appuser.name = :value', {
288
- value: 'John'
289
- });
290
- });
291
- });
292
- describe('getGlobalSearchQuery', ()=>{
293
- it('adds a LIKE search clause across name/email/phone', async ()=>{
294
- const qb = {
295
- andWhere: jest.fn().mockReturnThis()
296
- };
297
- await service.getGlobalSearchQuery(qb, 'john', null);
298
- expect(qb.andWhere).toHaveBeenCalledWith(expect.stringContaining('appuser.name LIKE'), {
299
- search: '%john%'
300
- });
301
- });
302
- });
303
- describe('getExtraManipulateQuery', ()=>{
304
- function buildQb() {
305
- return {
306
- andWhere: jest.fn().mockReturnThis(),
307
- addSelect: jest.fn().mockReturnThis(),
308
- setParameter: jest.fn().mockReturnThis()
309
- };
310
- }
311
- it('short-circuits with 1=0 when company feature is enabled but the user has no companyId', async ()=>{
312
- companyFeatureEnabled = true;
313
- const qb = buildQb();
314
- const { isRaw } = await service.getExtraManipulateQuery(qb, {
315
- filter: {
316
- actionCode: 'user.read'
317
- }
318
- }, (0, _loggedusermock.buildMockUser)({
319
- companyId: undefined
320
- }));
321
- expect(qb.andWhere).toHaveBeenCalledWith('1 = 0');
322
- expect(isRaw).toBe(true);
323
- });
324
- it('applies isActive filter directly on the non-company path', async ()=>{
325
- const qb = buildQb();
326
- await service.getExtraManipulateQuery(qb, {
327
- filter: {
328
- isActive: true
329
- }
330
- }, null);
331
- expect(qb.andWhere).toHaveBeenCalledWith('appuser.isActive = :value', {
332
- value: true
333
- });
334
- });
335
- it('restricts results to the current company when the company feature is enabled', async ()=>{
336
- companyFeatureEnabled = true;
337
- const qb = buildQb();
338
- service.permissionRepository = permissionRepo;
339
- const { isRaw } = await service.getExtraManipulateQuery(qb, {
340
- filter: {}
341
- }, (0, _loggedusermock.buildMockUser)({
342
- companyId: 'company-1'
343
- }));
344
- expect(qb.addSelect).toHaveBeenCalled();
345
- expect(qb.andWhere).toHaveBeenCalledWith(expect.stringContaining('user_company_permissions'), expect.objectContaining({
346
- permissionType: _usercompanypermissionentity.PermissionType.COMPANY,
347
- companyId: 'company-1'
348
- }));
349
- expect(isRaw).toBe(true);
350
- });
351
- });
352
- describe('insert', ()=>{
353
- it('delegates to the base insert when the company feature is disabled', async ()=>{
354
- const baseInsertSpy = jest.spyOn(_classes.ApiService.prototype, 'insert').mockResolvedValue({
355
- id: 'user-1'
356
- });
357
- const result = await service.insert({
358
- name: 'A',
359
- email: 'a@example.com',
360
- password: 'Password1!'
361
- }, null);
362
- expect(baseInsertSpy).toHaveBeenCalled();
363
- expect(result).toEqual({
364
- id: 'user-1'
365
- });
366
- baseInsertSpy.mockRestore();
367
- });
368
- it('creates the user and company/branch permissions transactionally when the company feature is enabled', async ()=>{
369
- companyFeatureEnabled = true;
370
- service.dataSource = mockDataSource;
371
- service.permissionRepository = permissionRepo;
372
- service.repository = userRepo;
373
- userRepo.create.mockReturnValue({});
374
- const savedUser = buildAppUser();
375
- const queryRunner = {
376
- connect: jest.fn(),
377
- startTransaction: jest.fn(),
378
- commitTransaction: jest.fn(),
379
- rollbackTransaction: jest.fn(),
380
- release: jest.fn(),
381
- manager: {
382
- save: jest.fn().mockResolvedValue(savedUser),
383
- findOne: jest.fn().mockResolvedValue(null),
384
- create: jest.fn().mockImplementation((_target, data)=>data)
385
- }
386
- };
387
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
388
- const result = await service.insert({
389
- name: 'A',
390
- email: 'a@example.com',
391
- password: 'Password1!',
392
- isActive: true
393
- }, (0, _loggedusermock.buildMockUser)({
394
- companyId: 'company-1',
395
- branchId: 'branch-1'
396
- }));
397
- expect(queryRunner.manager.save).toHaveBeenCalled();
398
- expect(queryRunner.commitTransaction).toHaveBeenCalled();
399
- expect(result).toEqual(expect.objectContaining({
400
- id: 'user-1'
401
- }));
402
- expectCacheCleared(savedUser.id);
403
- });
404
- it('rolls back the transaction if permission assignment fails', async ()=>{
405
- companyFeatureEnabled = true;
406
- service.dataSource = mockDataSource;
407
- service.permissionRepository = permissionRepo;
408
- service.repository = userRepo;
409
- userRepo.create.mockReturnValue({});
410
- const queryRunner = {
411
- connect: jest.fn(),
412
- startTransaction: jest.fn(),
413
- commitTransaction: jest.fn(),
414
- rollbackTransaction: jest.fn(),
415
- release: jest.fn(),
416
- manager: {
417
- save: jest.fn().mockResolvedValue(buildAppUser()),
418
- findOne: jest.fn().mockRejectedValue(new Error('db failure')),
419
- create: jest.fn()
420
- }
421
- };
422
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
423
- await expect(service.insert({
424
- name: 'A',
425
- email: 'a@example.com',
426
- password: 'Password1!'
427
- }, (0, _loggedusermock.buildMockUser)({
428
- companyId: 'company-1'
429
- }))).rejects.toThrow('db failure');
430
- expect(queryRunner.rollbackTransaction).toHaveBeenCalled();
431
- });
432
- });
433
- describe('getProfileWithExtras', ()=>{
434
- it('throws ForbiddenException when requesting another user profile', async ()=>{
435
- await expect(service.getProfileWithExtras('other-user', (0, _loggedusermock.buildMockUser)({
436
- id: 'user-1'
437
- }))).rejects.toThrow(_common.ForbiddenException);
438
- });
439
- it('throws NotFoundException when the user does not exist', async ()=>{
440
- userRepo.findOne.mockResolvedValue(null);
441
- await expect(service.getProfileWithExtras('user-1', (0, _loggedusermock.buildMockUser)({
442
- id: 'user-1'
443
- }))).rejects.toThrow(_common.NotFoundException);
444
- });
445
- it('returns the profile with enricher-provided extras', async ()=>{
446
- userRepo.findOne.mockResolvedValue(buildAppUser());
447
- mockUserEnricher.getProfileExtras.mockResolvedValue({
448
- completionPercentage: 80
449
- });
450
- const result = await service.getProfileWithExtras('user-1', (0, _loggedusermock.buildMockUser)({
451
- id: 'user-1'
452
- }));
453
- expect(result.user).toEqual(expect.objectContaining({
454
- id: 'user-1'
455
- }));
456
- expect(result.extras).toEqual({
457
- completionPercentage: 80
458
- });
459
- });
460
- });
461
- describe('getAll (enriched)', ()=>{
462
- it('enriches the list returned by the base getAll', async ()=>{
463
- jest.spyOn(_classes.ApiService.prototype, 'getAll').mockResolvedValue({
464
- data: [
465
- {
466
- id: 'user-1'
467
- }
468
- ],
469
- total: 1
470
- });
471
- mockUserEnricher.enrichListItems.mockResolvedValue([
472
- {
473
- id: 'user-1',
474
- completionPercentage: 50
475
- }
476
- ]);
477
- const result = await service.getAll('', {}, null);
478
- expect(result.data).toEqual([
479
- {
480
- id: 'user-1',
481
- completionPercentage: 50
482
- }
483
- ]);
484
- expect(result.total).toBe(1);
485
- jest.restoreAllMocks();
486
- });
487
- });
488
- describe('getEnrichedList', ()=>{
489
- it('returns users unchanged when no enricher is configured', async ()=>{
490
- service = new _userservice.UserService({}, mockUtilsService, mockDataSourceProvider, mockAuthConfig, undefined);
491
- const users = [
492
- {
493
- id: 'user-1'
494
- }
495
- ];
496
- const result = await service.getEnrichedList(users, null);
497
- expect(result).toBe(users);
498
- });
499
- });
500
- describe('getProfileSections / getProfileSectionData / getProfileCompletion', ()=>{
501
- it('returns sections from the enricher', async ()=>{
502
- mockUserEnricher.getProfileSections.mockReturnValue([
503
- {
504
- id: 's1',
505
- name: 'Basic',
506
- order: 1
507
- }
508
- ]);
509
- const result = await service.getProfileSections();
510
- expect(result).toEqual([
511
- {
512
- id: 's1',
513
- name: 'Basic',
514
- order: 1
515
- }
516
- ]);
517
- });
518
- it('returns an empty array when no enricher is configured', async ()=>{
519
- service = new _userservice.UserService({}, mockUtilsService, mockDataSourceProvider, mockAuthConfig, undefined);
520
- expect(await service.getProfileSections()).toEqual([]);
521
- });
522
- it('returns section data via the enricher after verifying profile ownership', async ()=>{
523
- userRepo.findOne.mockResolvedValue(buildAppUser());
524
- mockUserEnricher.getProfileSectionData.mockResolvedValue({
525
- sectionId: 's1',
526
- data: {},
527
- isComplete: true
528
- });
529
- const result = await service.getProfileSectionData('user-1', 's1', (0, _loggedusermock.buildMockUser)({
530
- id: 'user-1'
531
- }));
532
- expect(result).toEqual(expect.objectContaining({
533
- sectionId: 's1'
534
- }));
535
- });
536
- it('returns the default 100% completion when no enricher is configured', async ()=>{
537
- userRepo.findOne.mockResolvedValue(buildAppUser());
538
- service = new _userservice.UserService({}, mockUtilsService, mockDataSourceProvider, mockAuthConfig, undefined);
539
- const result = await service.getProfileCompletion('user-1', (0, _loggedusermock.buildMockUser)({
540
- id: 'user-1'
541
- }));
542
- expect(result).toBe(100);
543
- });
544
- it('delegates completion calculation to the enricher when configured', async ()=>{
545
- userRepo.findOne.mockResolvedValue(buildAppUser());
546
- mockUserEnricher.calculateProfileCompletion.mockResolvedValue(65);
547
- const result = await service.getProfileCompletion('user-1', (0, _loggedusermock.buildMockUser)({
548
- id: 'user-1'
549
- }));
550
- expect(result).toBe(65);
551
- });
552
- });
553
- describe('updateProfileSection', ()=>{
554
- it('throws BadRequestException when the data source is unavailable', async ()=>{
555
- userRepo.findOne.mockResolvedValue(buildAppUser());
556
- service.dataSource = undefined;
557
- mockDataSourceProvider.getDataSource.mockResolvedValue(undefined);
558
- await expect(service.updateProfileSection('user-1', 's1', {}, (0, _loggedusermock.buildMockUser)({
559
- id: 'user-1'
560
- }))).rejects.toThrow(_common.BadRequestException);
561
- });
562
- it('throws BadRequestException when profile sections are not supported', async ()=>{
563
- userRepo.findOne.mockResolvedValue(buildAppUser());
564
- service.dataSource = mockDataSource;
565
- service = new _userservice.UserService({}, mockUtilsService, mockDataSourceProvider, mockAuthConfig, undefined);
566
- service.dataSource = mockDataSource;
567
- await expect(service.updateProfileSection('user-1', 's1', {}, (0, _loggedusermock.buildMockUser)({
568
- id: 'user-1'
569
- }))).rejects.toThrow(_common.BadRequestException);
570
- });
571
- it('updates the section within a transaction and commits on success', async ()=>{
572
- userRepo.findOne.mockResolvedValue(buildAppUser());
573
- service.dataSource = mockDataSource;
574
- const queryRunner = {
575
- connect: jest.fn(),
576
- startTransaction: jest.fn(),
577
- commitTransaction: jest.fn(),
578
- rollbackTransaction: jest.fn(),
579
- release: jest.fn()
580
- };
581
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
582
- await service.updateProfileSection('user-1', 's1', {
583
- foo: 'bar'
584
- }, (0, _loggedusermock.buildMockUser)({
585
- id: 'user-1'
586
- }));
587
- expect(mockUserEnricher.updateProfileSection).toHaveBeenCalledWith('user-1', 's1', {
588
- foo: 'bar'
589
- }, queryRunner);
590
- expect(queryRunner.commitTransaction).toHaveBeenCalled();
591
- expectCacheCleared('user-1');
592
- });
593
- it('rolls back the transaction when the enricher throws', async ()=>{
594
- userRepo.findOne.mockResolvedValue(buildAppUser());
595
- service.dataSource = mockDataSource;
596
- const queryRunner = {
597
- connect: jest.fn(),
598
- startTransaction: jest.fn(),
599
- commitTransaction: jest.fn(),
600
- rollbackTransaction: jest.fn(),
601
- release: jest.fn()
602
- };
603
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
604
- mockUserEnricher.updateProfileSection.mockRejectedValue(new Error('enrich failed'));
605
- await expect(service.updateProfileSection('user-1', 's1', {}, (0, _loggedusermock.buildMockUser)({
606
- id: 'user-1'
607
- }))).rejects.toThrow('enrich failed');
608
- expect(queryRunner.rollbackTransaction).toHaveBeenCalled();
609
- });
610
- });
611
- describe('profile', ()=>{
612
- function buildQueryRunner(findUser) {
613
- return {
614
- connect: jest.fn(),
615
- startTransaction: jest.fn(),
616
- commitTransaction: jest.fn(),
617
- rollbackTransaction: jest.fn(),
618
- release: jest.fn(),
619
- manager: {
620
- findOne: jest.fn().mockResolvedValue(findUser),
621
- save: jest.fn().mockResolvedValue(findUser)
622
- }
623
- };
624
- }
625
- it('throws BadRequestException when the data source is unavailable', async ()=>{
626
- service.dataSource = undefined;
627
- mockDataSourceProvider.getDataSource.mockResolvedValue(undefined);
628
- await expect(service.profile({
629
- id: 'user-1'
630
- }, (0, _loggedusermock.buildMockUser)({
631
- id: 'user-1'
632
- }))).rejects.toThrow(_common.BadRequestException);
633
- });
634
- it('throws NotFoundException when the target user does not exist', async ()=>{
635
- service.dataSource = mockDataSource;
636
- const queryRunner = buildQueryRunner(null);
637
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
638
- await expect(service.profile({
639
- id: 'user-1'
640
- }, (0, _loggedusermock.buildMockUser)({
641
- id: 'user-1'
642
- }))).rejects.toThrow(_common.NotFoundException);
643
- expect(queryRunner.rollbackTransaction).toHaveBeenCalled();
644
- });
645
- it('requires the current password when changing your own password', async ()=>{
646
- service.dataSource = mockDataSource;
647
- const queryRunner = buildQueryRunner(buildAppUser());
648
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
649
- await expect(service.profile({
650
- id: 'user-1',
651
- password: 'NewPassword1!'
652
- }, (0, _loggedusermock.buildMockUser)({
653
- id: 'user-1'
654
- }))).rejects.toThrow(_common.BadRequestException);
655
- });
656
- it('rejects an incorrect current password when changing your own password', async ()=>{
657
- service.dataSource = mockDataSource;
658
- const queryRunner = buildQueryRunner(buildAppUser());
659
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
660
- _bcrypt.compare.mockResolvedValue(false);
661
- await expect(service.profile({
662
- id: 'user-1',
663
- password: 'NewPassword1!',
664
- oldPassword: 'wrong'
665
- }, (0, _loggedusermock.buildMockUser)({
666
- id: 'user-1'
667
- }))).rejects.toThrow(_common.BadRequestException);
668
- });
669
- it('updates the profile and hashes the new password on success', async ()=>{
670
- service.dataSource = mockDataSource;
671
- const queryRunner = buildQueryRunner(buildAppUser());
672
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
673
- const result = await service.profile({
674
- id: 'user-1',
675
- name: 'Updated Name',
676
- password: 'NewPassword1!',
677
- oldPassword: 'old-password'
678
- }, (0, _loggedusermock.buildMockUser)({
679
- id: 'user-1'
680
- }));
681
- expect(_bcrypt.hash).toHaveBeenCalledWith('NewPassword1!', 12);
682
- expect(queryRunner.manager.save).toHaveBeenCalled();
683
- expect(queryRunner.commitTransaction).toHaveBeenCalled();
684
- expect(result).toBe('user-1');
685
- expectCacheCleared('user-1');
686
- });
687
- it('does not require the current password when an admin updates another user profile', async ()=>{
688
- service.dataSource = mockDataSource;
689
- const targetUser = buildAppUser({
690
- id: 'other-user'
691
- });
692
- const queryRunner = buildQueryRunner(targetUser);
693
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
694
- await service.profile({
695
- id: 'other-user',
696
- password: 'NewPassword1!'
697
- }, (0, _loggedusermock.buildMockUser)({
698
- id: 'admin-1'
699
- }));
700
- expect(queryRunner.manager.save).toHaveBeenCalled();
701
- expect(queryRunner.commitTransaction).toHaveBeenCalled();
702
- expectCacheCleared('other-user');
703
- });
704
- it('rolls back the transaction when save fails', async ()=>{
705
- service.dataSource = mockDataSource;
706
- const queryRunner = buildQueryRunner(buildAppUser());
707
- queryRunner.manager.save.mockRejectedValue(new Error('save failed'));
708
- mockDataSource.createQueryRunner.mockReturnValue(queryRunner);
709
- await expect(service.profile({
710
- id: 'user-1',
711
- name: 'X'
712
- }, (0, _loggedusermock.buildMockUser)({
713
- id: 'user-1'
714
- }))).rejects.toThrow('save failed');
715
- expect(queryRunner.rollbackTransaction).toHaveBeenCalled();
716
- });
717
- });
718
- });