mongodb-dynamic-api 4.4.0 → 4.6.0

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 (144) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -1
  3. package/package.json +2 -3
  4. package/src/builders/route-decorators/route-decorators.builder.js +3 -4
  5. package/src/builders/route-decorators/route-decorators.builder.js.map +1 -1
  6. package/src/gateways/base.gateway.js +1 -2
  7. package/src/gateways/base.gateway.js.map +1 -1
  8. package/src/guards/jwt-socket.guard.js +2 -2
  9. package/src/guards/jwt-socket.guard.js.map +1 -1
  10. package/src/helpers/format.helper.js +2 -2
  11. package/src/helpers/format.helper.js.map +1 -1
  12. package/src/helpers/index.d.ts +1 -0
  13. package/src/helpers/index.js +1 -0
  14. package/src/helpers/index.js.map +1 -1
  15. package/src/helpers/lodash.helper.d.ts +9 -0
  16. package/src/helpers/lodash.helper.js +85 -0
  17. package/src/helpers/lodash.helper.js.map +1 -0
  18. package/src/helpers/mixin-data.helper.js +2 -2
  19. package/src/helpers/mixin-data.helper.js.map +1 -1
  20. package/src/helpers/route-description.helper.js +2 -2
  21. package/src/helpers/route-description.helper.js.map +1 -1
  22. package/src/interfaces/dynamic-api-service-before-save-callback.interface.d.ts +9 -9
  23. package/src/interfaces/dynamic-api-service-callback.interface.d.ts +2 -2
  24. package/src/modules/auth/auth-broadcast.helper.js +2 -2
  25. package/src/modules/auth/auth-broadcast.helper.js.map +1 -1
  26. package/src/modules/auth/guards/jwt-socket-auth/jwt-socket-auth.guard.js +2 -2
  27. package/src/modules/auth/guards/jwt-socket-auth/jwt-socket-auth.guard.js.map +1 -1
  28. package/src/modules/auth/guards/jwt-socket-refresh/jwt-socket-refresh.guard.js +2 -2
  29. package/src/modules/auth/guards/jwt-socket-refresh/jwt-socket-refresh.guard.js.map +1 -1
  30. package/src/modules/auth/mixins/auth-gateway.mixin.js +2 -3
  31. package/src/modules/auth/mixins/auth-gateway.mixin.js.map +1 -1
  32. package/src/routes/aggregate/aggregate-controller.interface.d.ts +1 -1
  33. package/src/routes/aggregate/aggregate-controller.mixin.js +4 -3
  34. package/src/routes/aggregate/aggregate-controller.mixin.js.map +1 -1
  35. package/src/routes/aggregate/aggregate-gateway.mixin.js +1 -1
  36. package/src/routes/aggregate/aggregate-gateway.mixin.js.map +1 -1
  37. package/src/routes/aggregate/aggregate-service.interface.d.ts +1 -1
  38. package/src/routes/aggregate/base-aggregate.service.d.ts +1 -1
  39. package/src/routes/aggregate/base-aggregate.service.js +2 -2
  40. package/src/routes/aggregate/base-aggregate.service.js.map +1 -1
  41. package/src/routes/create-many/base-create-many.service.d.ts +1 -1
  42. package/src/routes/create-many/base-create-many.service.js +5 -5
  43. package/src/routes/create-many/base-create-many.service.js.map +1 -1
  44. package/src/routes/create-many/create-many-controller.interface.d.ts +1 -1
  45. package/src/routes/create-many/create-many-controller.mixin.js +5 -5
  46. package/src/routes/create-many/create-many-controller.mixin.js.map +1 -1
  47. package/src/routes/create-many/create-many-gateway.mixin.js +2 -3
  48. package/src/routes/create-many/create-many-gateway.mixin.js.map +1 -1
  49. package/src/routes/create-many/create-many-service.interface.d.ts +1 -1
  50. package/src/routes/create-one/base-create-one.service.d.ts +1 -1
  51. package/src/routes/create-one/base-create-one.service.js +5 -5
  52. package/src/routes/create-one/base-create-one.service.js.map +1 -1
  53. package/src/routes/create-one/create-one-controller.interface.d.ts +1 -1
  54. package/src/routes/create-one/create-one-controller.mixin.js +4 -3
  55. package/src/routes/create-one/create-one-controller.mixin.js.map +1 -1
  56. package/src/routes/create-one/create-one-gateway.mixin.js +2 -3
  57. package/src/routes/create-one/create-one-gateway.mixin.js.map +1 -1
  58. package/src/routes/create-one/create-one-service.interface.d.ts +1 -1
  59. package/src/routes/delete-many/base-delete-many.service.d.ts +1 -1
  60. package/src/routes/delete-many/base-delete-many.service.js +3 -3
  61. package/src/routes/delete-many/base-delete-many.service.js.map +1 -1
  62. package/src/routes/delete-many/delete-many-controller.interface.d.ts +1 -1
  63. package/src/routes/delete-many/delete-many-controller.mixin.js +4 -3
  64. package/src/routes/delete-many/delete-many-controller.mixin.js.map +1 -1
  65. package/src/routes/delete-many/delete-many-gateway.mixin.js +1 -1
  66. package/src/routes/delete-many/delete-many-gateway.mixin.js.map +1 -1
  67. package/src/routes/delete-many/delete-many-service.interface.d.ts +1 -1
  68. package/src/routes/delete-one/base-delete-one.service.d.ts +1 -1
  69. package/src/routes/delete-one/base-delete-one.service.js +3 -3
  70. package/src/routes/delete-one/base-delete-one.service.js.map +1 -1
  71. package/src/routes/delete-one/delete-one-controller.interface.d.ts +1 -1
  72. package/src/routes/delete-one/delete-one-controller.mixin.js +4 -3
  73. package/src/routes/delete-one/delete-one-controller.mixin.js.map +1 -1
  74. package/src/routes/delete-one/delete-one-gateway.mixin.js +1 -1
  75. package/src/routes/delete-one/delete-one-gateway.mixin.js.map +1 -1
  76. package/src/routes/delete-one/delete-one-service.interface.d.ts +1 -1
  77. package/src/routes/duplicate-many/base-duplicate-many.service.d.ts +1 -1
  78. package/src/routes/duplicate-many/base-duplicate-many.service.js +4 -4
  79. package/src/routes/duplicate-many/base-duplicate-many.service.js.map +1 -1
  80. package/src/routes/duplicate-many/duplicate-many-controller.interface.d.ts +1 -1
  81. package/src/routes/duplicate-many/duplicate-many-controller.mixin.js +4 -4
  82. package/src/routes/duplicate-many/duplicate-many-controller.mixin.js.map +1 -1
  83. package/src/routes/duplicate-many/duplicate-many-gateway.mixin.js +1 -2
  84. package/src/routes/duplicate-many/duplicate-many-gateway.mixin.js.map +1 -1
  85. package/src/routes/duplicate-many/duplicate-many-service.interface.d.ts +1 -1
  86. package/src/routes/duplicate-one/base-duplicate-one.service.d.ts +1 -1
  87. package/src/routes/duplicate-one/base-duplicate-one.service.js +4 -4
  88. package/src/routes/duplicate-one/base-duplicate-one.service.js.map +1 -1
  89. package/src/routes/duplicate-one/duplicate-one-controller.interface.d.ts +1 -1
  90. package/src/routes/duplicate-one/duplicate-one-controller.mixin.js +4 -4
  91. package/src/routes/duplicate-one/duplicate-one-controller.mixin.js.map +1 -1
  92. package/src/routes/duplicate-one/duplicate-one-gateway.mixin.js +1 -2
  93. package/src/routes/duplicate-one/duplicate-one-gateway.mixin.js.map +1 -1
  94. package/src/routes/duplicate-one/duplicate-one-service.interface.d.ts +1 -1
  95. package/src/routes/get-many/base-get-many.service.d.ts +1 -1
  96. package/src/routes/get-many/base-get-many.service.js +2 -2
  97. package/src/routes/get-many/base-get-many.service.js.map +1 -1
  98. package/src/routes/get-many/get-many-controller.interface.d.ts +1 -1
  99. package/src/routes/get-many/get-many-controller.mixin.js +4 -3
  100. package/src/routes/get-many/get-many-controller.mixin.js.map +1 -1
  101. package/src/routes/get-many/get-many-gateway.mixin.js +1 -1
  102. package/src/routes/get-many/get-many-gateway.mixin.js.map +1 -1
  103. package/src/routes/get-many/get-many-service.interface.d.ts +1 -1
  104. package/src/routes/get-one/base-get-one.service.d.ts +1 -1
  105. package/src/routes/get-one/base-get-one.service.js +2 -2
  106. package/src/routes/get-one/base-get-one.service.js.map +1 -1
  107. package/src/routes/get-one/get-one-controller.interface.d.ts +1 -1
  108. package/src/routes/get-one/get-one-controller.mixin.js +4 -3
  109. package/src/routes/get-one/get-one-controller.mixin.js.map +1 -1
  110. package/src/routes/get-one/get-one-gateway.mixin.js +1 -1
  111. package/src/routes/get-one/get-one-gateway.mixin.js.map +1 -1
  112. package/src/routes/get-one/get-one-service.interface.d.ts +1 -1
  113. package/src/routes/replace-one/base-replace-one.service.d.ts +1 -1
  114. package/src/routes/replace-one/base-replace-one.service.js +5 -5
  115. package/src/routes/replace-one/base-replace-one.service.js.map +1 -1
  116. package/src/routes/replace-one/replace-one-controller.interface.d.ts +1 -1
  117. package/src/routes/replace-one/replace-one-controller.mixin.js +4 -3
  118. package/src/routes/replace-one/replace-one-controller.mixin.js.map +1 -1
  119. package/src/routes/replace-one/replace-one-gateway.mixin.js +1 -1
  120. package/src/routes/replace-one/replace-one-gateway.mixin.js.map +1 -1
  121. package/src/routes/replace-one/replace-one-service.interface.d.ts +1 -1
  122. package/src/routes/update-many/base-update-many.service.d.ts +1 -1
  123. package/src/routes/update-many/base-update-many.service.js +4 -4
  124. package/src/routes/update-many/base-update-many.service.js.map +1 -1
  125. package/src/routes/update-many/update-many-controller.interface.d.ts +1 -1
  126. package/src/routes/update-many/update-many-controller.mixin.js +5 -5
  127. package/src/routes/update-many/update-many-controller.mixin.js.map +1 -1
  128. package/src/routes/update-many/update-many-gateway.mixin.js +2 -3
  129. package/src/routes/update-many/update-many-gateway.mixin.js.map +1 -1
  130. package/src/routes/update-many/update-many-service.interface.d.ts +1 -1
  131. package/src/routes/update-one/base-update-one.service.d.ts +1 -1
  132. package/src/routes/update-one/base-update-one.service.js +5 -5
  133. package/src/routes/update-one/base-update-one.service.js.map +1 -1
  134. package/src/routes/update-one/update-one-controller.interface.d.ts +1 -1
  135. package/src/routes/update-one/update-one-controller.mixin.js +5 -5
  136. package/src/routes/update-one/update-one-controller.mixin.js.map +1 -1
  137. package/src/routes/update-one/update-one-gateway.mixin.js +1 -1
  138. package/src/routes/update-one/update-one-gateway.mixin.js.map +1 -1
  139. package/src/routes/update-one/update-one-service.interface.d.ts +1 -1
  140. package/src/version.json +1 -1
  141. package/test/for-feature/callbacks-user.e2e-spec.d.ts +1 -0
  142. package/test/for-feature/callbacks-user.e2e-spec.js +772 -0
  143. package/test/for-feature/callbacks-user.e2e-spec.js.map +1 -0
  144. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,772 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const mongoose_1 = require("@nestjs/mongoose");
13
+ const testing_1 = require("@nestjs/testing");
14
+ const mongoose_2 = require("mongoose");
15
+ const src_1 = require("../../src");
16
+ const e2e_setup_1 = require("../e2e.setup");
17
+ require("dotenv/config");
18
+ describe('Callbacks receive authenticated user (e2e)', () => {
19
+ let UserEntity = class UserEntity extends src_1.BaseEntity {
20
+ };
21
+ __decorate([
22
+ (0, mongoose_1.Prop)({ type: String, required: true }),
23
+ __metadata("design:type", String)
24
+ ], UserEntity.prototype, "email", void 0);
25
+ __decorate([
26
+ (0, mongoose_1.Prop)({ type: String, required: true }),
27
+ __metadata("design:type", String)
28
+ ], UserEntity.prototype, "password", void 0);
29
+ UserEntity = __decorate([
30
+ (0, mongoose_1.Schema)({ collection: 'users-cb' })
31
+ ], UserEntity);
32
+ let ItemEntity = class ItemEntity extends src_1.BaseEntity {
33
+ };
34
+ __decorate([
35
+ (0, mongoose_1.Prop)({ type: String, required: true }),
36
+ __metadata("design:type", String)
37
+ ], ItemEntity.prototype, "name", void 0);
38
+ __decorate([
39
+ (0, mongoose_1.Prop)({ type: String }),
40
+ __metadata("design:type", String)
41
+ ], ItemEntity.prototype, "createdBy", void 0);
42
+ __decorate([
43
+ (0, mongoose_1.Prop)({ type: String }),
44
+ __metadata("design:type", String)
45
+ ], ItemEntity.prototype, "updatedBy", void 0);
46
+ ItemEntity = __decorate([
47
+ (0, mongoose_1.Schema)({ collection: 'items-cb' })
48
+ ], ItemEntity);
49
+ let AuditLogEntity = class AuditLogEntity extends src_1.BaseEntity {
50
+ };
51
+ __decorate([
52
+ (0, mongoose_1.Prop)({ type: String, required: true }),
53
+ __metadata("design:type", String)
54
+ ], AuditLogEntity.prototype, "action", void 0);
55
+ __decorate([
56
+ (0, mongoose_1.Prop)({ type: String, required: true }),
57
+ __metadata("design:type", String)
58
+ ], AuditLogEntity.prototype, "entityId", void 0);
59
+ __decorate([
60
+ (0, mongoose_1.Prop)({ type: String }),
61
+ __metadata("design:type", String)
62
+ ], AuditLogEntity.prototype, "performedBy", void 0);
63
+ AuditLogEntity = __decorate([
64
+ (0, mongoose_1.Schema)({ collection: 'audit-logs-cb' })
65
+ ], AuditLogEntity);
66
+ const createOneBeforeSave = async (_entity, context, _methods, user) => {
67
+ const u = user;
68
+ return { ...context.toCreate, createdBy: u?.email ?? 'anonymous' };
69
+ };
70
+ const createOneAfterSave = async (entity, methods, user) => {
71
+ const u = user;
72
+ await methods.createOneDocument(AuditLogEntity, {
73
+ action: 'CreateOne',
74
+ entityId: entity.id,
75
+ performedBy: u?.email ?? 'anonymous',
76
+ });
77
+ };
78
+ const createManyBeforeSave = async (_entities, context, _methods, user) => {
79
+ const u = user;
80
+ return context.toCreate.map((item) => ({ ...item, createdBy: u?.email ?? 'anonymous' }));
81
+ };
82
+ const createManyAfterSave = async (entity, methods, user) => {
83
+ const u = user;
84
+ await methods.createOneDocument(AuditLogEntity, {
85
+ action: 'CreateMany',
86
+ entityId: entity.id,
87
+ performedBy: u?.email ?? 'anonymous',
88
+ });
89
+ };
90
+ const updateOneBeforeSave = async (_entity, context, _methods, user) => {
91
+ const u = user;
92
+ return { ...context.update, updatedBy: u?.email ?? 'anonymous' };
93
+ };
94
+ const updateOneAfterSave = async (entity, methods, user) => {
95
+ const u = user;
96
+ await methods.createOneDocument(AuditLogEntity, {
97
+ action: 'UpdateOne',
98
+ entityId: entity.id,
99
+ performedBy: u?.email ?? 'anonymous',
100
+ });
101
+ };
102
+ const updateManyBeforeSave = async (entities, context, _methods, user) => {
103
+ const u = user;
104
+ return entities.map(() => ({ ...context.update, updatedBy: u?.email ?? 'anonymous' }));
105
+ };
106
+ const updateManyAfterSave = async (entity, methods, user) => {
107
+ const u = user;
108
+ await methods.createOneDocument(AuditLogEntity, {
109
+ action: 'UpdateMany',
110
+ entityId: entity.id,
111
+ performedBy: u?.email ?? 'anonymous',
112
+ });
113
+ };
114
+ const replaceOneBeforeSave = async (_entity, context, _methods, user) => {
115
+ const u = user;
116
+ return { ...context.replacement, updatedBy: u?.email ?? 'anonymous' };
117
+ };
118
+ const replaceOneAfterSave = async (entity, methods, user) => {
119
+ const u = user;
120
+ await methods.createOneDocument(AuditLogEntity, {
121
+ action: 'ReplaceOne',
122
+ entityId: entity.id,
123
+ performedBy: u?.email ?? 'anonymous',
124
+ });
125
+ };
126
+ const duplicateOneBeforeSave = async (_entity, context, _methods, user) => {
127
+ const u = user;
128
+ return { ...(context.override ?? {}), createdBy: u?.email ?? 'anonymous' };
129
+ };
130
+ const duplicateOneAfterSave = async (entity, methods, user) => {
131
+ const u = user;
132
+ await methods.createOneDocument(AuditLogEntity, {
133
+ action: 'DuplicateOne',
134
+ entityId: entity.id,
135
+ performedBy: u?.email ?? 'anonymous',
136
+ });
137
+ };
138
+ const duplicateManyBeforeSave = async (entities, context, _methods, user) => {
139
+ const u = user;
140
+ return entities.map((e) => ({
141
+ name: e.name,
142
+ ...(context.override ?? {}),
143
+ createdBy: u?.email ?? 'anonymous',
144
+ }));
145
+ };
146
+ const duplicateManyAfterSave = async (entity, methods, user) => {
147
+ const u = user;
148
+ await methods.createOneDocument(AuditLogEntity, {
149
+ action: 'DuplicateMany',
150
+ entityId: entity.id,
151
+ performedBy: u?.email ?? 'anonymous',
152
+ });
153
+ };
154
+ const deleteOneBeforeSave = async (entity, _context, methods, user) => {
155
+ const u = user;
156
+ if (entity) {
157
+ await methods.createOneDocument(AuditLogEntity, {
158
+ action: 'DeleteOne-before',
159
+ entityId: entity.id,
160
+ performedBy: u?.email ?? 'anonymous',
161
+ });
162
+ }
163
+ };
164
+ const deleteOneAfterSave = async (entity, methods, user) => {
165
+ const u = user;
166
+ await methods.createOneDocument(AuditLogEntity, {
167
+ action: 'DeleteOne',
168
+ entityId: entity.id,
169
+ performedBy: u?.email ?? 'anonymous',
170
+ });
171
+ };
172
+ const deleteManyBeforeSave = async (entities, _context, methods, user) => {
173
+ const u = user;
174
+ for (const entity of entities) {
175
+ await methods.createOneDocument(AuditLogEntity, {
176
+ action: 'DeleteMany-before',
177
+ entityId: entity._id?.toString() ?? entity.id,
178
+ performedBy: u?.email ?? 'anonymous',
179
+ });
180
+ }
181
+ };
182
+ const deleteManyAfterSave = async (entity, methods, user) => {
183
+ const u = user;
184
+ await methods.createOneDocument(AuditLogEntity, {
185
+ action: 'DeleteMany',
186
+ entityId: entity.id,
187
+ performedBy: u?.email ?? 'anonymous',
188
+ });
189
+ };
190
+ const getOneAfterSave = async (entity, methods, user) => {
191
+ const u = user;
192
+ await methods.createOneDocument(AuditLogEntity, {
193
+ action: 'GetOne',
194
+ entityId: entity.id,
195
+ performedBy: u?.email ?? 'anonymous',
196
+ });
197
+ };
198
+ const getManyAfterSave = async (entity, methods, user) => {
199
+ const u = user;
200
+ await methods.createOneDocument(AuditLogEntity, {
201
+ action: 'GetMany',
202
+ entityId: entity.id,
203
+ performedBy: u?.email ?? 'anonymous',
204
+ });
205
+ };
206
+ const routes = [
207
+ { type: 'CreateOne', callback: createOneAfterSave, beforeSaveCallback: createOneBeforeSave },
208
+ { type: 'CreateMany', callback: createManyAfterSave, beforeSaveCallback: createManyBeforeSave },
209
+ { type: 'UpdateOne', callback: updateOneAfterSave, beforeSaveCallback: updateOneBeforeSave },
210
+ { type: 'UpdateMany', callback: updateManyAfterSave, beforeSaveCallback: updateManyBeforeSave },
211
+ { type: 'ReplaceOne', callback: replaceOneAfterSave, beforeSaveCallback: replaceOneBeforeSave },
212
+ { type: 'DuplicateOne', callback: duplicateOneAfterSave, beforeSaveCallback: duplicateOneBeforeSave },
213
+ { type: 'DuplicateMany', callback: duplicateManyAfterSave, beforeSaveCallback: duplicateManyBeforeSave },
214
+ { type: 'DeleteOne', callback: deleteOneAfterSave, beforeSaveCallback: deleteOneBeforeSave },
215
+ { type: 'DeleteMany', callback: deleteManyAfterSave, beforeSaveCallback: deleteManyBeforeSave },
216
+ { type: 'GetOne', callback: getOneAfterSave },
217
+ { type: 'GetMany', callback: getManyAfterSave },
218
+ ];
219
+ let accessToken;
220
+ const userEmail = 'cb-test@test.co';
221
+ const userPassword = 'test';
222
+ beforeEach(async () => {
223
+ src_1.DynamicApiModule.state['resetState']();
224
+ const uri = process.env.MONGO_DB_URL;
225
+ const moduleRef = await testing_1.Test.createTestingModule({
226
+ imports: [
227
+ src_1.DynamicApiModule.forRoot(uri, { useAuth: { userEntity: UserEntity } }),
228
+ src_1.DynamicApiModule.forFeature({
229
+ entity: ItemEntity,
230
+ controllerOptions: { path: 'items' },
231
+ routes,
232
+ }),
233
+ src_1.DynamicApiModule.forFeature({
234
+ entity: AuditLogEntity,
235
+ controllerOptions: { path: 'audit-logs' },
236
+ }),
237
+ ],
238
+ }).compile();
239
+ await (0, e2e_setup_1.createTestingApp)(moduleRef);
240
+ await e2e_setup_1.server.post('/auth/register', { email: userEmail, password: userPassword });
241
+ const { body } = await e2e_setup_1.server.post('/auth/login', { email: userEmail, password: userPassword });
242
+ accessToken = body.accessToken;
243
+ });
244
+ afterEach(async () => {
245
+ await (0, e2e_setup_1.closeTestingApp)(mongoose_2.default.connections);
246
+ });
247
+ const auth = () => ({ authToken: accessToken });
248
+ const getAuditLogs = async (action) => {
249
+ const { body } = await e2e_setup_1.server.get('/audit-logs', auth());
250
+ return body.filter((log) => log.action === action);
251
+ };
252
+ describe('CreateOne', () => {
253
+ it('beforeSaveCallback should set createdBy from user and afterSave should create audit log', async () => {
254
+ const { body, status } = await e2e_setup_1.server.post('/items', { name: 'item-1' }, auth());
255
+ expect(status).toBe(201);
256
+ expect(body.createdBy).toBe(userEmail);
257
+ const logs = await getAuditLogs('CreateOne');
258
+ expect(logs).toHaveLength(1);
259
+ expect(logs[0]).toEqual(expect.objectContaining({
260
+ action: 'CreateOne',
261
+ entityId: body.id,
262
+ performedBy: userEmail,
263
+ }));
264
+ });
265
+ });
266
+ describe('CreateMany', () => {
267
+ it('beforeSaveCallback should set createdBy from user and afterSave should create audit logs', async () => {
268
+ const { body, status } = await e2e_setup_1.server.post('/items/many', { list: [{ name: 'item-a' }, { name: 'item-b' }] }, auth());
269
+ expect(status).toBe(201);
270
+ expect(body).toHaveLength(2);
271
+ expect(body[0].createdBy).toBe(userEmail);
272
+ expect(body[1].createdBy).toBe(userEmail);
273
+ const logs = await getAuditLogs('CreateMany');
274
+ expect(logs).toHaveLength(2);
275
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
276
+ });
277
+ });
278
+ describe('UpdateOne', () => {
279
+ it('beforeSaveCallback should set updatedBy from user and afterSave should create audit log', async () => {
280
+ const { body: created } = await e2e_setup_1.server.post('/items', { name: 'to-update' }, auth());
281
+ const { body: updated, status } = await e2e_setup_1.server.patch(`/items/${created.id}`, { name: 'updated-name' }, auth());
282
+ expect(status).toBe(200);
283
+ expect(updated.updatedBy).toBe(userEmail);
284
+ expect(updated.name).toBe('updated-name');
285
+ const logs = await getAuditLogs('UpdateOne');
286
+ expect(logs).toHaveLength(1);
287
+ expect(logs[0].performedBy).toBe(userEmail);
288
+ });
289
+ });
290
+ describe('UpdateMany', () => {
291
+ it('beforeSaveCallback should set updatedBy from user and afterSave should create audit logs', async () => {
292
+ const { body: items } = await e2e_setup_1.server.post('/items/many', { list: [{ name: 'um-1' }, { name: 'um-2' }] }, auth());
293
+ const ids = items.map((i) => i.id);
294
+ const { body: updated, status } = await e2e_setup_1.server.patch('/items', { name: 'bulk-updated' }, { ...auth(), query: { ids } });
295
+ expect(status).toBe(200);
296
+ expect(updated).toHaveLength(2);
297
+ expect(updated.every((u) => u.updatedBy === userEmail)).toBe(true);
298
+ const logs = await getAuditLogs('UpdateMany');
299
+ expect(logs).toHaveLength(2);
300
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
301
+ });
302
+ });
303
+ describe('ReplaceOne', () => {
304
+ it('beforeSaveCallback should set updatedBy from user and afterSave should create audit log', async () => {
305
+ const { body: created } = await e2e_setup_1.server.post('/items', { name: 'to-replace' }, auth());
306
+ const { body: replaced, status } = await e2e_setup_1.server.put(`/items/${created.id}`, { name: 'replaced-name' }, auth());
307
+ expect(status).toBe(200);
308
+ expect(replaced.updatedBy).toBe(userEmail);
309
+ expect(replaced.name).toBe('replaced-name');
310
+ const logs = await getAuditLogs('ReplaceOne');
311
+ expect(logs).toHaveLength(1);
312
+ expect(logs[0].performedBy).toBe(userEmail);
313
+ });
314
+ });
315
+ describe('DuplicateOne', () => {
316
+ it('beforeSaveCallback should set createdBy from user and afterSave should create audit log', async () => {
317
+ const { body: created } = await e2e_setup_1.server.post('/items', { name: 'to-dup' }, auth());
318
+ const { body: duplicated, status } = await e2e_setup_1.server.post(`/items/duplicate/${created.id}`, { name: 'dup-name' }, auth());
319
+ expect(status).toBe(201);
320
+ expect(duplicated.createdBy).toBe(userEmail);
321
+ const logs = await getAuditLogs('DuplicateOne');
322
+ expect(logs).toHaveLength(1);
323
+ expect(logs[0]).toEqual(expect.objectContaining({
324
+ entityId: duplicated.id,
325
+ performedBy: userEmail,
326
+ }));
327
+ });
328
+ });
329
+ describe('DuplicateMany', () => {
330
+ it('beforeSaveCallback should set createdBy from user and afterSave should create audit logs', async () => {
331
+ const { body: items } = await e2e_setup_1.server.post('/items/many', { list: [{ name: 'dm-1' }, { name: 'dm-2' }] }, auth());
332
+ const ids = items.map((i) => i.id);
333
+ const { body: duplicated, status } = await e2e_setup_1.server.post('/items/duplicate', {}, { ...auth(), query: { ids } });
334
+ expect(status).toBe(201);
335
+ expect(duplicated).toHaveLength(2);
336
+ expect(duplicated.every((d) => d.createdBy === userEmail)).toBe(true);
337
+ const logs = await getAuditLogs('DuplicateMany');
338
+ expect(logs).toHaveLength(2);
339
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
340
+ });
341
+ });
342
+ describe('DeleteOne', () => {
343
+ it('beforeSaveCallback and afterSave should both create audit logs with user', async () => {
344
+ const { body: created } = await e2e_setup_1.server.post('/items', { name: 'to-delete' }, auth());
345
+ const { body, status } = await e2e_setup_1.server.delete(`/items/${created.id}`, auth());
346
+ expect(status).toBe(200);
347
+ expect(body.deletedCount).toBe(1);
348
+ const beforeLogs = await getAuditLogs('DeleteOne-before');
349
+ expect(beforeLogs).toHaveLength(1);
350
+ expect(beforeLogs[0].performedBy).toBe(userEmail);
351
+ const afterLogs = await getAuditLogs('DeleteOne');
352
+ expect(afterLogs).toHaveLength(1);
353
+ expect(afterLogs[0]).toEqual(expect.objectContaining({
354
+ entityId: created.id,
355
+ performedBy: userEmail,
356
+ }));
357
+ });
358
+ });
359
+ describe('DeleteMany', () => {
360
+ it('beforeSaveCallback and afterSave should both create audit logs with user', async () => {
361
+ const { body: items } = await e2e_setup_1.server.post('/items/many', { list: [{ name: 'del-1' }, { name: 'del-2' }] }, auth());
362
+ const ids = items.map((i) => i.id);
363
+ const { body, status } = await e2e_setup_1.server.delete('/items', { ...auth(), query: { ids } });
364
+ expect(status).toBe(200);
365
+ expect(body.deletedCount).toBe(2);
366
+ const beforeLogs = await getAuditLogs('DeleteMany-before');
367
+ expect(beforeLogs).toHaveLength(2);
368
+ expect(beforeLogs.every((l) => l.performedBy === userEmail)).toBe(true);
369
+ const afterLogs = await getAuditLogs('DeleteMany');
370
+ expect(afterLogs).toHaveLength(2);
371
+ expect(afterLogs.every((l) => l.performedBy === userEmail)).toBe(true);
372
+ });
373
+ });
374
+ describe('GetOne', () => {
375
+ it('afterSave callback should create audit log with user', async () => {
376
+ const { body: created } = await e2e_setup_1.server.post('/items', { name: 'get-one-item' }, auth());
377
+ const { status } = await e2e_setup_1.server.get(`/items/${created.id}`, auth());
378
+ expect(status).toBe(200);
379
+ const logs = await getAuditLogs('GetOne');
380
+ expect(logs).toHaveLength(1);
381
+ expect(logs[0]).toEqual(expect.objectContaining({
382
+ entityId: created.id,
383
+ performedBy: userEmail,
384
+ }));
385
+ });
386
+ });
387
+ describe('GetMany', () => {
388
+ it('afterSave callback should create audit logs with user for each returned entity', async () => {
389
+ await e2e_setup_1.server.post('/items/many', { list: [{ name: 'gm-1' }, { name: 'gm-2' }] }, auth());
390
+ const { body: items, status } = await e2e_setup_1.server.get('/items', auth());
391
+ expect(status).toBe(200);
392
+ const logs = await getAuditLogs('GetMany');
393
+ expect(logs.length).toBe(items.length);
394
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
395
+ });
396
+ });
397
+ });
398
+ describe('Callbacks receive authenticated user via WebSocket (e2e)', () => {
399
+ let UserEntity = class UserEntity extends src_1.BaseEntity {
400
+ };
401
+ __decorate([
402
+ (0, mongoose_1.Prop)({ type: String, required: true }),
403
+ __metadata("design:type", String)
404
+ ], UserEntity.prototype, "email", void 0);
405
+ __decorate([
406
+ (0, mongoose_1.Prop)({ type: String, required: true }),
407
+ __metadata("design:type", String)
408
+ ], UserEntity.prototype, "password", void 0);
409
+ UserEntity = __decorate([
410
+ (0, mongoose_1.Schema)({ collection: 'users-cb-ws' })
411
+ ], UserEntity);
412
+ let ItemEntity = class ItemEntity extends src_1.BaseEntity {
413
+ };
414
+ __decorate([
415
+ (0, mongoose_1.Prop)({ type: String, required: true }),
416
+ __metadata("design:type", String)
417
+ ], ItemEntity.prototype, "name", void 0);
418
+ __decorate([
419
+ (0, mongoose_1.Prop)({ type: String }),
420
+ __metadata("design:type", String)
421
+ ], ItemEntity.prototype, "createdBy", void 0);
422
+ __decorate([
423
+ (0, mongoose_1.Prop)({ type: String }),
424
+ __metadata("design:type", String)
425
+ ], ItemEntity.prototype, "updatedBy", void 0);
426
+ ItemEntity = __decorate([
427
+ (0, mongoose_1.Schema)({ collection: 'items-cb-ws' })
428
+ ], ItemEntity);
429
+ let AuditLogEntity = class AuditLogEntity extends src_1.BaseEntity {
430
+ };
431
+ __decorate([
432
+ (0, mongoose_1.Prop)({ type: String, required: true }),
433
+ __metadata("design:type", String)
434
+ ], AuditLogEntity.prototype, "action", void 0);
435
+ __decorate([
436
+ (0, mongoose_1.Prop)({ type: String, required: true }),
437
+ __metadata("design:type", String)
438
+ ], AuditLogEntity.prototype, "entityId", void 0);
439
+ __decorate([
440
+ (0, mongoose_1.Prop)({ type: String }),
441
+ __metadata("design:type", String)
442
+ ], AuditLogEntity.prototype, "performedBy", void 0);
443
+ AuditLogEntity = __decorate([
444
+ (0, mongoose_1.Schema)({ collection: 'audit-logs-cb-ws' })
445
+ ], AuditLogEntity);
446
+ const createOneBeforeSave = async (_entity, context, _methods, user) => {
447
+ const u = user;
448
+ return { ...context.toCreate, createdBy: u?.email ?? 'anonymous' };
449
+ };
450
+ const createOneAfterSave = async (entity, methods, user) => {
451
+ const u = user;
452
+ await methods.createOneDocument(AuditLogEntity, {
453
+ action: 'WS-CreateOne',
454
+ entityId: entity.id,
455
+ performedBy: u?.email ?? 'anonymous',
456
+ });
457
+ };
458
+ const createManyBeforeSave = async (_entities, context, _methods, user) => {
459
+ const u = user;
460
+ return context.toCreate.map((item) => ({ ...item, createdBy: u?.email ?? 'anonymous' }));
461
+ };
462
+ const createManyAfterSave = async (entity, methods, user) => {
463
+ const u = user;
464
+ await methods.createOneDocument(AuditLogEntity, {
465
+ action: 'WS-CreateMany',
466
+ entityId: entity.id,
467
+ performedBy: u?.email ?? 'anonymous',
468
+ });
469
+ };
470
+ const updateOneBeforeSave = async (_entity, context, _methods, user) => {
471
+ const u = user;
472
+ return { ...context.update, updatedBy: u?.email ?? 'anonymous' };
473
+ };
474
+ const updateOneAfterSave = async (entity, methods, user) => {
475
+ const u = user;
476
+ await methods.createOneDocument(AuditLogEntity, {
477
+ action: 'WS-UpdateOne',
478
+ entityId: entity.id,
479
+ performedBy: u?.email ?? 'anonymous',
480
+ });
481
+ };
482
+ const updateManyBeforeSave = async (entities, context, _methods, user) => {
483
+ const u = user;
484
+ return entities.map(() => ({ ...context.update, updatedBy: u?.email ?? 'anonymous' }));
485
+ };
486
+ const updateManyAfterSave = async (entity, methods, user) => {
487
+ const u = user;
488
+ await methods.createOneDocument(AuditLogEntity, {
489
+ action: 'WS-UpdateMany',
490
+ entityId: entity.id,
491
+ performedBy: u?.email ?? 'anonymous',
492
+ });
493
+ };
494
+ const replaceOneBeforeSave = async (_entity, context, _methods, user) => {
495
+ const u = user;
496
+ return { ...context.replacement, updatedBy: u?.email ?? 'anonymous' };
497
+ };
498
+ const replaceOneAfterSave = async (entity, methods, user) => {
499
+ const u = user;
500
+ await methods.createOneDocument(AuditLogEntity, {
501
+ action: 'WS-ReplaceOne',
502
+ entityId: entity.id,
503
+ performedBy: u?.email ?? 'anonymous',
504
+ });
505
+ };
506
+ const duplicateOneBeforeSave = async (_entity, context, _methods, user) => {
507
+ const u = user;
508
+ return { ...(context.override ?? {}), createdBy: u?.email ?? 'anonymous' };
509
+ };
510
+ const duplicateOneAfterSave = async (entity, methods, user) => {
511
+ const u = user;
512
+ await methods.createOneDocument(AuditLogEntity, {
513
+ action: 'WS-DuplicateOne',
514
+ entityId: entity.id,
515
+ performedBy: u?.email ?? 'anonymous',
516
+ });
517
+ };
518
+ const duplicateManyBeforeSave = async (entities, context, _methods, user) => {
519
+ const u = user;
520
+ return entities.map((e) => ({
521
+ name: e.name,
522
+ ...(context.override ?? {}),
523
+ createdBy: u?.email ?? 'anonymous',
524
+ }));
525
+ };
526
+ const duplicateManyAfterSave = async (entity, methods, user) => {
527
+ const u = user;
528
+ await methods.createOneDocument(AuditLogEntity, {
529
+ action: 'WS-DuplicateMany',
530
+ entityId: entity.id,
531
+ performedBy: u?.email ?? 'anonymous',
532
+ });
533
+ };
534
+ const deleteOneBeforeSave = async (entity, _context, methods, user) => {
535
+ const u = user;
536
+ if (entity) {
537
+ await methods.createOneDocument(AuditLogEntity, {
538
+ action: 'WS-DeleteOne-before',
539
+ entityId: entity.id,
540
+ performedBy: u?.email ?? 'anonymous',
541
+ });
542
+ }
543
+ };
544
+ const deleteOneAfterSave = async (entity, methods, user) => {
545
+ const u = user;
546
+ await methods.createOneDocument(AuditLogEntity, {
547
+ action: 'WS-DeleteOne',
548
+ entityId: entity.id,
549
+ performedBy: u?.email ?? 'anonymous',
550
+ });
551
+ };
552
+ const deleteManyBeforeSave = async (entities, _context, methods, user) => {
553
+ const u = user;
554
+ for (const entity of entities) {
555
+ await methods.createOneDocument(AuditLogEntity, {
556
+ action: 'WS-DeleteMany-before',
557
+ entityId: entity._id?.toString() ?? entity.id,
558
+ performedBy: u?.email ?? 'anonymous',
559
+ });
560
+ }
561
+ };
562
+ const deleteManyAfterSave = async (entity, methods, user) => {
563
+ const u = user;
564
+ await methods.createOneDocument(AuditLogEntity, {
565
+ action: 'WS-DeleteMany',
566
+ entityId: entity.id,
567
+ performedBy: u?.email ?? 'anonymous',
568
+ });
569
+ };
570
+ const getOneAfterSave = async (entity, methods, user) => {
571
+ const u = user;
572
+ await methods.createOneDocument(AuditLogEntity, {
573
+ action: 'WS-GetOne',
574
+ entityId: entity.id,
575
+ performedBy: u?.email ?? 'anonymous',
576
+ });
577
+ };
578
+ const getManyAfterSave = async (entity, methods, user) => {
579
+ const u = user;
580
+ await methods.createOneDocument(AuditLogEntity, {
581
+ action: 'WS-GetMany',
582
+ entityId: entity.id,
583
+ performedBy: u?.email ?? 'anonymous',
584
+ });
585
+ };
586
+ const wsRoutes = [
587
+ { type: 'CreateOne', webSocket: true, callback: createOneAfterSave, beforeSaveCallback: createOneBeforeSave },
588
+ { type: 'CreateMany', webSocket: true, callback: createManyAfterSave, beforeSaveCallback: createManyBeforeSave },
589
+ { type: 'UpdateOne', webSocket: true, callback: updateOneAfterSave, beforeSaveCallback: updateOneBeforeSave },
590
+ { type: 'UpdateMany', webSocket: true, callback: updateManyAfterSave, beforeSaveCallback: updateManyBeforeSave },
591
+ { type: 'ReplaceOne', webSocket: true, callback: replaceOneAfterSave, beforeSaveCallback: replaceOneBeforeSave },
592
+ { type: 'DuplicateOne', webSocket: true, callback: duplicateOneAfterSave, beforeSaveCallback: duplicateOneBeforeSave },
593
+ { type: 'DuplicateMany', webSocket: true, callback: duplicateManyAfterSave, beforeSaveCallback: duplicateManyBeforeSave },
594
+ { type: 'DeleteOne', webSocket: true, callback: deleteOneAfterSave, beforeSaveCallback: deleteOneBeforeSave },
595
+ { type: 'DeleteMany', webSocket: true, callback: deleteManyAfterSave, beforeSaveCallback: deleteManyBeforeSave },
596
+ { type: 'GetOne', webSocket: true, callback: getOneAfterSave },
597
+ { type: 'GetMany', webSocket: true, callback: getManyAfterSave },
598
+ ];
599
+ let accessToken;
600
+ const userEmail = 'ws-cb-test@test.co';
601
+ const userPassword = 'test';
602
+ beforeEach(async () => {
603
+ src_1.DynamicApiModule.state['resetState']();
604
+ const uri = process.env.MONGO_DB_URL;
605
+ const moduleRef = await testing_1.Test.createTestingModule({
606
+ imports: [
607
+ src_1.DynamicApiModule.forRoot(uri, {
608
+ useAuth: { userEntity: UserEntity, webSocket: true },
609
+ }),
610
+ src_1.DynamicApiModule.forFeature({
611
+ entity: ItemEntity,
612
+ controllerOptions: { path: 'items' },
613
+ routes: wsRoutes,
614
+ }),
615
+ src_1.DynamicApiModule.forFeature({
616
+ entity: AuditLogEntity,
617
+ controllerOptions: { path: 'audit-logs' },
618
+ }),
619
+ ],
620
+ }).compile();
621
+ await (0, e2e_setup_1.createTestingApp)(moduleRef, undefined, async (app) => {
622
+ app.useWebSocketAdapter(new e2e_setup_1.TestSocketAdapter(app));
623
+ });
624
+ const registerResult = await e2e_setup_1.server.emit('auth-register', { email: userEmail, password: userPassword });
625
+ accessToken = registerResult.accessToken;
626
+ });
627
+ afterEach(async () => {
628
+ await (0, e2e_setup_1.closeTestingApp)(mongoose_2.default.connections);
629
+ });
630
+ const wsAuth = () => ({ accessToken });
631
+ const getAuditLogs = async (action) => {
632
+ const { body } = await e2e_setup_1.server.get('/audit-logs', { authToken: accessToken });
633
+ return body.filter((log) => log.action === action);
634
+ };
635
+ describe('CreateOne', () => {
636
+ it('beforeSaveCallback should set createdBy from socket user and afterSave should create audit log', async () => {
637
+ const result = await e2e_setup_1.server.emit('create-one-item-entity', { name: 'ws-item-1' }, wsAuth());
638
+ expect(result).toEqual(expect.objectContaining({ name: 'ws-item-1', createdBy: userEmail }));
639
+ const logs = await getAuditLogs('WS-CreateOne');
640
+ expect(logs).toHaveLength(1);
641
+ expect(logs[0]).toEqual(expect.objectContaining({
642
+ action: 'WS-CreateOne',
643
+ entityId: result.id,
644
+ performedBy: userEmail,
645
+ }));
646
+ });
647
+ });
648
+ describe('CreateMany', () => {
649
+ it('beforeSaveCallback should set createdBy from socket user and afterSave should create audit logs', async () => {
650
+ const result = await e2e_setup_1.server.emit('create-many-item-entity', { list: [{ name: 'ws-a' }, { name: 'ws-b' }] }, wsAuth());
651
+ expect(result).toHaveLength(2);
652
+ expect(result[0].createdBy).toBe(userEmail);
653
+ expect(result[1].createdBy).toBe(userEmail);
654
+ const logs = await getAuditLogs('WS-CreateMany');
655
+ expect(logs).toHaveLength(2);
656
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
657
+ });
658
+ });
659
+ describe('UpdateOne', () => {
660
+ it('beforeSaveCallback should set updatedBy from socket user and afterSave should create audit log', async () => {
661
+ const created = await e2e_setup_1.server.emit('create-one-item-entity', { name: 'ws-to-update' }, wsAuth());
662
+ const updated = await e2e_setup_1.server.emit('update-one-item-entity', { id: created.id, name: 'ws-updated' }, wsAuth());
663
+ expect(updated.updatedBy).toBe(userEmail);
664
+ expect(updated.name).toBe('ws-updated');
665
+ const logs = await getAuditLogs('WS-UpdateOne');
666
+ expect(logs).toHaveLength(1);
667
+ expect(logs[0].performedBy).toBe(userEmail);
668
+ });
669
+ });
670
+ describe('UpdateMany', () => {
671
+ it('beforeSaveCallback should set updatedBy from socket user and afterSave should create audit logs', async () => {
672
+ const items = await e2e_setup_1.server.emit('create-many-item-entity', { list: [{ name: 'ws-um-1' }, { name: 'ws-um-2' }] }, wsAuth());
673
+ const ids = items.map((i) => i.id);
674
+ const updated = await e2e_setup_1.server.emit('update-many-item-entity', { ids, name: 'ws-bulk-updated' }, wsAuth());
675
+ expect(updated).toHaveLength(2);
676
+ expect(updated.every((u) => u.updatedBy === userEmail)).toBe(true);
677
+ const logs = await getAuditLogs('WS-UpdateMany');
678
+ expect(logs).toHaveLength(2);
679
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
680
+ });
681
+ });
682
+ describe('ReplaceOne', () => {
683
+ it('beforeSaveCallback should set updatedBy from socket user and afterSave should create audit log', async () => {
684
+ const created = await e2e_setup_1.server.emit('create-one-item-entity', { name: 'ws-to-replace' }, wsAuth());
685
+ const replaced = await e2e_setup_1.server.emit('replace-one-item-entity', { id: created.id, name: 'ws-replaced' }, wsAuth());
686
+ expect(replaced.updatedBy).toBe(userEmail);
687
+ expect(replaced.name).toBe('ws-replaced');
688
+ const logs = await getAuditLogs('WS-ReplaceOne');
689
+ expect(logs).toHaveLength(1);
690
+ expect(logs[0].performedBy).toBe(userEmail);
691
+ });
692
+ });
693
+ describe('DuplicateOne', () => {
694
+ it('beforeSaveCallback should set createdBy from socket user and afterSave should create audit log', async () => {
695
+ const created = await e2e_setup_1.server.emit('create-one-item-entity', { name: 'ws-to-dup' }, wsAuth());
696
+ const duplicated = await e2e_setup_1.server.emit('duplicate-one-item-entity', { id: created.id, name: 'ws-dup-name' }, wsAuth());
697
+ expect(duplicated.createdBy).toBe(userEmail);
698
+ const logs = await getAuditLogs('WS-DuplicateOne');
699
+ expect(logs).toHaveLength(1);
700
+ expect(logs[0]).toEqual(expect.objectContaining({
701
+ entityId: duplicated.id,
702
+ performedBy: userEmail,
703
+ }));
704
+ });
705
+ });
706
+ describe('DuplicateMany', () => {
707
+ it('beforeSaveCallback should set createdBy from socket user and afterSave should create audit logs', async () => {
708
+ const items = await e2e_setup_1.server.emit('create-many-item-entity', { list: [{ name: 'ws-dm-1' }, { name: 'ws-dm-2' }] }, wsAuth());
709
+ const ids = items.map((i) => i.id);
710
+ const duplicated = await e2e_setup_1.server.emit('duplicate-many-item-entity', { ids }, wsAuth());
711
+ expect(duplicated).toHaveLength(2);
712
+ expect(duplicated.every((d) => d.createdBy === userEmail)).toBe(true);
713
+ const logs = await getAuditLogs('WS-DuplicateMany');
714
+ expect(logs).toHaveLength(2);
715
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
716
+ });
717
+ });
718
+ describe('DeleteOne', () => {
719
+ it('beforeSaveCallback and afterSave should both create audit logs with socket user', async () => {
720
+ const created = await e2e_setup_1.server.emit('create-one-item-entity', { name: 'ws-to-delete' }, wsAuth());
721
+ const deleteResult = await e2e_setup_1.server.emit('delete-one-item-entity', { id: created.id }, wsAuth());
722
+ expect(deleteResult.deletedCount).toBe(1);
723
+ const beforeLogs = await getAuditLogs('WS-DeleteOne-before');
724
+ expect(beforeLogs).toHaveLength(1);
725
+ expect(beforeLogs[0].performedBy).toBe(userEmail);
726
+ const afterLogs = await getAuditLogs('WS-DeleteOne');
727
+ expect(afterLogs).toHaveLength(1);
728
+ expect(afterLogs[0]).toEqual(expect.objectContaining({
729
+ entityId: created.id,
730
+ performedBy: userEmail,
731
+ }));
732
+ });
733
+ });
734
+ describe('DeleteMany', () => {
735
+ it('beforeSaveCallback and afterSave should both create audit logs with socket user', async () => {
736
+ const items = await e2e_setup_1.server.emit('create-many-item-entity', { list: [{ name: 'ws-del-1' }, { name: 'ws-del-2' }] }, wsAuth());
737
+ const ids = items.map((i) => i.id);
738
+ const deleteResult = await e2e_setup_1.server.emit('delete-many-item-entity', { ids }, wsAuth());
739
+ expect(deleteResult.deletedCount).toBe(2);
740
+ const beforeLogs = await getAuditLogs('WS-DeleteMany-before');
741
+ expect(beforeLogs).toHaveLength(2);
742
+ expect(beforeLogs.every((l) => l.performedBy === userEmail)).toBe(true);
743
+ const afterLogs = await getAuditLogs('WS-DeleteMany');
744
+ expect(afterLogs).toHaveLength(2);
745
+ expect(afterLogs.every((l) => l.performedBy === userEmail)).toBe(true);
746
+ });
747
+ });
748
+ describe('GetOne', () => {
749
+ it('afterSave callback should create audit log with socket user', async () => {
750
+ const created = await e2e_setup_1.server.emit('create-one-item-entity', { name: 'ws-get-one-item' }, wsAuth());
751
+ const result = await e2e_setup_1.server.emit('get-one-item-entity', { id: created.id }, wsAuth());
752
+ expect(result).toEqual(expect.objectContaining({ id: created.id }));
753
+ const logs = await getAuditLogs('WS-GetOne');
754
+ expect(logs).toHaveLength(1);
755
+ expect(logs[0]).toEqual(expect.objectContaining({
756
+ entityId: created.id,
757
+ performedBy: userEmail,
758
+ }));
759
+ });
760
+ });
761
+ describe('GetMany', () => {
762
+ it('afterSave callback should create audit logs with socket user for each returned entity', async () => {
763
+ const items = await e2e_setup_1.server.emit('create-many-item-entity', { list: [{ name: 'ws-gm-1' }, { name: 'ws-gm-2' }] }, wsAuth());
764
+ const result = await e2e_setup_1.server.emit('get-many-item-entity', {}, wsAuth());
765
+ expect(result.length).toBeGreaterThanOrEqual(items.length);
766
+ const logs = await getAuditLogs('WS-GetMany');
767
+ expect(logs.length).toBe(result.length);
768
+ expect(logs.every((l) => l.performedBy === userEmail)).toBe(true);
769
+ });
770
+ });
771
+ });
772
+ //# sourceMappingURL=callbacks-user.e2e-spec.js.map