@drax/crud-back 0.37.5 → 0.38.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.
@@ -1,8 +1,9 @@
1
1
  import { CommonConfig, DraxConfig, LimitError, NotFoundError, BadRequestError, CommonController } from "@drax/common-back";
2
2
  import { join } from "path";
3
3
  import QueryFilterRegex from "../regexs/QueryFilterRegex.js";
4
+ import CrudEventEmitter from "../events/CrudEventEmitter.js";
4
5
  class AbstractFastifyController extends CommonController {
5
- constructor(service, permission) {
6
+ constructor(service, permission, entityName) {
6
7
  super();
7
8
  this.baseFileDir = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
8
9
  this.baseURL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : '';
@@ -27,6 +28,8 @@ class AbstractFastifyController extends CommonController {
27
28
  this.maximumLimit = 10000;
28
29
  this.service = service;
29
30
  this.permission = permission;
31
+ this.entityName = entityName || this.constructor.name.replace('Fastify', '').replace('Controller', '');
32
+ this.eventEmitter = CrudEventEmitter.getInstance();
30
33
  console.log("AbstractFastifyController created. Permissions", this.permission);
31
34
  }
32
35
  parseFilters(stringFilters) {
@@ -93,12 +96,85 @@ class AbstractFastifyController extends CommonController {
93
96
  payload[this.userField] = rbac.userId;
94
97
  }
95
98
  }
99
+ extractRequestData(request) {
100
+ return {
101
+ user: {
102
+ id: request.rbac.userId,
103
+ username: request.rbac.username,
104
+ role: {
105
+ id: request.rbac.roleId,
106
+ name: request.rbac.roleName,
107
+ },
108
+ tenant: {
109
+ id: request.rbac.tenantId,
110
+ name: request.rbac.tenantName,
111
+ },
112
+ apiKey: {
113
+ id: request.rbac.apiKeyId,
114
+ name: request.rbac.apiKeyName,
115
+ }
116
+ },
117
+ ip: request.ip,
118
+ userAgent: request.headers['user-agent'],
119
+ sessionId: request.rbac.session,
120
+ requestId: request.id,
121
+ };
122
+ }
123
+ async onCreated(request, item) {
124
+ const requestData = this.extractRequestData(request);
125
+ const eventData = {
126
+ action: 'created',
127
+ entity: this.entityName,
128
+ postItem: item,
129
+ preItem: null,
130
+ timestamp: new Date(),
131
+ ...requestData
132
+ };
133
+ this.eventEmitter.emitCrudEvent(eventData);
134
+ }
135
+ async onUpdated(request, preItem, postItem) {
136
+ const requestData = this.extractRequestData(request);
137
+ const eventData = {
138
+ action: 'updated',
139
+ entity: this.entityName,
140
+ postItem: postItem,
141
+ preItem: preItem,
142
+ timestamp: new Date(),
143
+ ...requestData
144
+ };
145
+ this.eventEmitter.emitCrudEvent(eventData);
146
+ }
147
+ async onDeleted(request, item) {
148
+ const requestData = this.extractRequestData(request);
149
+ const eventData = {
150
+ action: 'deleted',
151
+ entity: this.entityName,
152
+ postItem: null,
153
+ preItem: item,
154
+ timestamp: new Date(),
155
+ ...requestData
156
+ };
157
+ this.eventEmitter.emitCrudEvent(eventData);
158
+ }
159
+ async onExported(request, response) {
160
+ const requestData = this.extractRequestData(request);
161
+ const eventData = {
162
+ action: 'exported',
163
+ entity: this.entityName,
164
+ postItem: null,
165
+ preItem: null,
166
+ timestamp: new Date(),
167
+ ...requestData
168
+ };
169
+ this.eventEmitter.emitCrudEvent(eventData);
170
+ }
96
171
  async create(request, reply) {
97
172
  try {
98
173
  request.rbac.assertPermission(this.permission.Create);
99
174
  const payload = request.body;
100
175
  this.applyUserAndTenantSetters(payload, request.rbac);
101
176
  let item = await this.service.create(payload);
177
+ await this.onCreated(request, item);
102
178
  return item;
103
179
  }
104
180
  catch (e) {
@@ -114,8 +190,8 @@ class AbstractFastifyController extends CommonController {
114
190
  }
115
191
  const id = request.params.id;
116
192
  const payload = request.body;
193
+ let preItem = await this.service.findById(id);
117
194
  if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
118
- let preItem = await this.service.findById(id);
119
195
  if (!preItem) {
120
196
  reply.statusCode = 404;
121
197
  reply.send({ error: 'NOT_FOUND' });
@@ -129,6 +205,7 @@ class AbstractFastifyController extends CommonController {
129
205
  if (!item) {
130
206
  throw new NotFoundError();
131
207
  }
208
+ await this.onUpdated(request, preItem, item);
132
209
  return item;
133
210
  }
134
211
  catch (e) {
@@ -144,8 +221,8 @@ class AbstractFastifyController extends CommonController {
144
221
  }
145
222
  const id = request.params.id;
146
223
  const payload = request.body;
224
+ let preItem = await this.service.findById(id);
147
225
  if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
148
- let preItem = await this.service.findById(id);
149
226
  if (!preItem) {
150
227
  reply.statusCode = 404;
151
228
  reply.send({ error: 'NOT_FOUND' });
@@ -159,6 +236,7 @@ class AbstractFastifyController extends CommonController {
159
236
  if (!item) {
160
237
  throw new NotFoundError();
161
238
  }
239
+ await this.onUpdated(request, preItem, item);
162
240
  return item;
163
241
  }
164
242
  catch (e) {
@@ -183,6 +261,7 @@ class AbstractFastifyController extends CommonController {
183
261
  }
184
262
  this.assertTenant(item, request.rbac);
185
263
  await this.service.delete(id);
264
+ await this.onDeleted(request, item);
186
265
  reply.send({
187
266
  id: id,
188
267
  message: 'Item deleted successfully',
@@ -371,12 +450,14 @@ class AbstractFastifyController extends CommonController {
371
450
  filters,
372
451
  }, destinationPath);
373
452
  const url = `${this.baseURL}/api/file/${exportPath}/${year}/${month}/${result.fileName}`;
374
- return {
453
+ const response = {
375
454
  url: url,
376
455
  rowCount: result.rowCount,
377
456
  time: result.time,
378
457
  fileName: result.fileName,
379
458
  };
459
+ await this.onExported(request, response);
460
+ return response;
380
461
  }
381
462
  catch (e) {
382
463
  this.handleError(e, reply);
@@ -0,0 +1,18 @@
1
+ import { EventEmitter } from 'events';
2
+ class CrudEventEmitter extends EventEmitter {
3
+ constructor() {
4
+ super();
5
+ this.setMaxListeners(100); // Aumentar el límite de listeners si es necesario
6
+ }
7
+ static getInstance() {
8
+ if (!CrudEventEmitter.instance) {
9
+ CrudEventEmitter.instance = new CrudEventEmitter();
10
+ }
11
+ return CrudEventEmitter.instance;
12
+ }
13
+ emitCrudEvent(eventData) {
14
+ this.emit('crud:event', eventData);
15
+ }
16
+ }
17
+ export default CrudEventEmitter;
18
+ export { CrudEventEmitter };
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import AbstractMongoRepository from "./repository/AbstractMongoRepository.js";
3
3
  import AbstractSqliteRepository from "./repository/AbstractSqliteRepository.js";
4
4
  import AbstractService from "./services/AbstractService.js";
5
5
  import AbstractFastifyController from "./controllers/AbstractFastifyController.js";
6
+ import { CrudEventEmitter } from "./events/CrudEventEmitter.js";
6
7
  //schemas
7
8
  import { IdParamSchema } from "./schemas/IdParamSchema.js";
8
9
  import { DeleteBodyResponseSchema } from "./schemas/DeleteBodyResponseSchema.js";
@@ -17,6 +18,8 @@ import { CrudSchemaBuilder } from "./builders/CrudSchemaBuilder.js";
17
18
  export {
18
19
  //CRUD
19
20
  AbstractMongoRepository, AbstractSqliteRepository, AbstractService, AbstractFastifyController,
21
+ //EVENTs
22
+ CrudEventEmitter,
20
23
  //Schemas
21
24
  IdParamSchema, DeleteBodyResponseSchema, PaginateBodyResponseSchema, PaginateQuerySchema, FindQuerySchema, GroupByQuerySchema, SearchQuerySchema, FindByParamSchema, ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema, ExportBodyResponseSchema,
22
25
  //Builder
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.37.5",
6
+ "version": "0.38.0",
7
7
  "description": "Crud utils across modules",
8
8
  "main": "dist/index.js",
9
9
  "types": "types/index.d.ts",
@@ -22,10 +22,10 @@
22
22
  "author": "Cristian Incarnato & Drax Team",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "@drax/common-back": "^0.37.2",
26
- "@drax/common-share": "^0.37.0",
27
- "@drax/identity-share": "^0.37.0",
28
- "@drax/media-back": "^0.37.5",
25
+ "@drax/common-back": "^0.38.0",
26
+ "@drax/common-share": "^0.38.0",
27
+ "@drax/identity-share": "^0.38.0",
28
+ "@drax/media-back": "^0.38.0",
29
29
  "@graphql-tools/load-files": "^7.0.0",
30
30
  "@graphql-tools/merge": "^9.0.4",
31
31
  "mongoose": "^8.6.3",
@@ -45,5 +45,5 @@
45
45
  "tsc-alias": "^1.8.10",
46
46
  "typescript": "^5.6.2"
47
47
  },
48
- "gitHead": "2fd3d1fa98a875f67691ea4524682985a52cba17"
48
+ "gitHead": "43c90f3c12165e7527edefbc80dd327a59236dd5"
49
49
  }
@@ -9,9 +9,16 @@ import {
9
9
  } from "@drax/common-back";
10
10
  import {IRbac} from "@drax/identity-share";
11
11
  import type {FastifyReply, FastifyRequest} from "fastify";
12
- import {IDraxExportResult, IDraxPermission, IDraxFieldFilter} from "@drax/crud-share";
12
+ import {
13
+ IDraxExportResult,
14
+ IDraxPermission,
15
+ IDraxFieldFilter,
16
+ IDraxExportResponse,
17
+ IDraxCrudEvent
18
+ } from "@drax/crud-share";
13
19
  import {join} from "path";
14
20
  import QueryFilterRegex from "../regexs/QueryFilterRegex.js";
21
+ import CrudEventEmitter from "../events/CrudEventEmitter.js";
15
22
 
16
23
  declare module 'fastify' {
17
24
  interface FastifyRequest {
@@ -53,6 +60,8 @@ class AbstractFastifyController<T, C, U> extends CommonController {
53
60
  protected baseFileDir = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
54
61
  protected baseURL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : ''
55
62
 
63
+ protected entityName: string
64
+
56
65
  protected tenantField: string = 'tenant'
57
66
  protected userField: string = 'user'
58
67
 
@@ -78,13 +87,19 @@ class AbstractFastifyController<T, C, U> extends CommonController {
78
87
  protected defaultLimit: number = 1000
79
88
  protected maximumLimit: number = 10000
80
89
 
81
- constructor(service: AbstractService<T, C, U>, permission: IDraxPermission) {
90
+ protected eventEmitter: CrudEventEmitter
91
+
92
+ constructor(service: AbstractService<T, C, U>, permission: IDraxPermission, entityName?: string) {
82
93
  super();
83
94
  this.service = service
84
95
  this.permission = permission
96
+ this.entityName = entityName || this.constructor.name.replace('Fastify', '').replace('Controller', '')
97
+ this.eventEmitter = CrudEventEmitter.getInstance()
85
98
  console.log("AbstractFastifyController created. Permissions", this.permission)
86
99
  }
87
100
 
101
+
102
+
88
103
  protected parseFilters(stringFilters: string): IDraxFieldFilter[] {
89
104
  try {
90
105
  if (!stringFilters) {
@@ -165,6 +180,83 @@ class AbstractFastifyController<T, C, U> extends CommonController {
165
180
  }
166
181
  }
167
182
 
183
+ protected extractRequestData(request: CustomRequest) {
184
+ return {
185
+ user: {
186
+ id: request.rbac.userId,
187
+ username: request.rbac.username,
188
+ role:{
189
+ id: request.rbac.roleId,
190
+ name: request.rbac.roleName,
191
+ },
192
+ tenant: {
193
+ id: request.rbac.tenantId,
194
+ name: request.rbac.tenantName,
195
+ },
196
+ apiKey: {
197
+ id: request.rbac.apiKeyId,
198
+ name: request.rbac.apiKeyName,
199
+ }
200
+ },
201
+ ip: request.ip,
202
+ userAgent: request.headers['user-agent'],
203
+ sessionId: request.rbac.session,
204
+ requestId: request.id,
205
+ };
206
+ }
207
+
208
+ async onCreated(request: CustomRequest, item:T){
209
+ const requestData = this.extractRequestData(request)
210
+ const eventData : IDraxCrudEvent = {
211
+ action: 'created',
212
+ entity: this.entityName,
213
+ postItem: item,
214
+ preItem: null,
215
+ timestamp: new Date(),
216
+ ...requestData
217
+ }
218
+ this.eventEmitter.emitCrudEvent(eventData)
219
+ }
220
+
221
+ async onUpdated(request: CustomRequest, preItem: T, postItem:T){
222
+ const requestData = this.extractRequestData(request)
223
+ const eventData : IDraxCrudEvent = {
224
+ action: 'updated',
225
+ entity: this.entityName,
226
+ postItem: postItem,
227
+ preItem: preItem,
228
+ timestamp: new Date(),
229
+ ...requestData
230
+ }
231
+ this.eventEmitter.emitCrudEvent(eventData)
232
+ }
233
+
234
+ async onDeleted(request: CustomRequest, item: T) {
235
+ const requestData = this.extractRequestData(request)
236
+ const eventData : IDraxCrudEvent = {
237
+ action: 'deleted',
238
+ entity: this.entityName,
239
+ postItem: null,
240
+ preItem: item,
241
+ timestamp: new Date(),
242
+ ...requestData
243
+ }
244
+ this.eventEmitter.emitCrudEvent(eventData)
245
+ }
246
+
247
+ async onExported(request: CustomRequest, response: IDraxExportResponse) {
248
+ const requestData = this.extractRequestData(request)
249
+ const eventData : IDraxCrudEvent = {
250
+ action: 'exported',
251
+ entity: this.entityName,
252
+ postItem: null,
253
+ preItem: null,
254
+ timestamp: new Date(),
255
+ ...requestData
256
+ }
257
+ this.eventEmitter.emitCrudEvent(eventData)
258
+ }
259
+
168
260
 
169
261
  async create(request: CustomRequest, reply: FastifyReply) {
170
262
  try {
@@ -172,12 +264,15 @@ class AbstractFastifyController<T, C, U> extends CommonController {
172
264
  const payload = request.body
173
265
  this.applyUserAndTenantSetters(payload, request.rbac)
174
266
  let item = await this.service.create(payload as C)
267
+ await this.onCreated(request, item)
175
268
  return item
176
269
  } catch (e) {
177
270
  this.handleError(e, reply)
178
271
  }
179
272
  }
180
273
 
274
+
275
+
181
276
  async update(request: CustomRequest, reply: FastifyReply) {
182
277
  try {
183
278
  request.rbac.assertPermission(this.permission.Update)
@@ -188,9 +283,9 @@ class AbstractFastifyController<T, C, U> extends CommonController {
188
283
  const id = request.params.id
189
284
  const payload = request.body
190
285
 
191
- if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
286
+ let preItem = await this.service.findById(id)
192
287
 
193
- let preItem = await this.service.findById(id)
288
+ if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
194
289
 
195
290
  if (!preItem) {
196
291
  reply.statusCode = 404
@@ -210,6 +305,8 @@ class AbstractFastifyController<T, C, U> extends CommonController {
210
305
  throw new NotFoundError()
211
306
  }
212
307
 
308
+ await this.onUpdated(request, preItem, item)
309
+
213
310
  return item
214
311
  } catch (e) {
215
312
  this.handleError(e, reply)
@@ -226,9 +323,9 @@ class AbstractFastifyController<T, C, U> extends CommonController {
226
323
  const id = request.params.id
227
324
  const payload = request.body
228
325
 
229
- if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
326
+ let preItem = await this.service.findById(id)
230
327
 
231
- let preItem = await this.service.findById(id)
328
+ if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
232
329
 
233
330
  if (!preItem) {
234
331
  reply.statusCode = 404
@@ -246,12 +343,15 @@ class AbstractFastifyController<T, C, U> extends CommonController {
246
343
  if (!item) {
247
344
  throw new NotFoundError()
248
345
  }
346
+ await this.onUpdated(request, preItem, item)
249
347
  return item
250
348
  } catch (e) {
251
349
  this.handleError(e, reply)
252
350
  }
253
351
  }
254
352
 
353
+
354
+
255
355
  async delete(request: CustomRequest, reply: FastifyReply) {
256
356
  try {
257
357
  request.rbac.assertPermission(this.permission.Delete)
@@ -277,6 +377,9 @@ class AbstractFastifyController<T, C, U> extends CommonController {
277
377
 
278
378
 
279
379
  await this.service.delete(id)
380
+
381
+ await this.onDeleted(request, item)
382
+
280
383
  reply.send({
281
384
  id: id,
282
385
  message: 'Item deleted successfully',
@@ -465,6 +568,8 @@ class AbstractFastifyController<T, C, U> extends CommonController {
465
568
  }
466
569
 
467
570
 
571
+
572
+
468
573
  async export(request: CustomRequest, reply: FastifyReply) {
469
574
  try {
470
575
  request.rbac.assertPermission(this.permission.View)
@@ -503,14 +608,17 @@ class AbstractFastifyController<T, C, U> extends CommonController {
503
608
 
504
609
  const url = `${this.baseURL}/api/file/${exportPath}/${year}/${month}/${result.fileName}`
505
610
 
506
-
507
- return {
611
+ const response : IDraxExportResponse = {
508
612
  url: url,
509
613
  rowCount: result.rowCount,
510
614
  time: result.time,
511
615
  fileName: result.fileName,
512
616
  }
513
617
 
618
+ await this.onExported(request, response)
619
+
620
+ return response
621
+
514
622
  } catch (e) {
515
623
  this.handleError(e, reply)
516
624
  }
@@ -0,0 +1,26 @@
1
+ import {EventEmitter} from 'events';
2
+ import {IDraxCrudEvent} from "@drax/crud-share";
3
+
4
+
5
+ class CrudEventEmitter extends EventEmitter {
6
+ private static instance: CrudEventEmitter;
7
+
8
+ private constructor() {
9
+ super();
10
+ this.setMaxListeners(100); // Aumentar el límite de listeners si es necesario
11
+ }
12
+
13
+ static getInstance(): CrudEventEmitter {
14
+ if (!CrudEventEmitter.instance) {
15
+ CrudEventEmitter.instance = new CrudEventEmitter();
16
+ }
17
+ return CrudEventEmitter.instance;
18
+ }
19
+
20
+ emitCrudEvent<T>(eventData: IDraxCrudEvent<T>): void {
21
+ this.emit('crud:event', eventData);
22
+ }
23
+ }
24
+
25
+ export default CrudEventEmitter;
26
+ export {CrudEventEmitter};
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import AbstractSqliteRepository from "./repository/AbstractSqliteRepository.js";
4
4
  import AbstractService from "./services/AbstractService.js";
5
5
  import AbstractFastifyController from "./controllers/AbstractFastifyController.js";
6
6
  import type {CustomRequest} from "./controllers/AbstractFastifyController.js";
7
+ import {CrudEventEmitter} from "./events/CrudEventEmitter.js";
7
8
 
8
9
  //schemas
9
10
  import {IdParamSchema} from "./schemas/IdParamSchema.js"
@@ -29,6 +30,9 @@ export {
29
30
  AbstractService,
30
31
  AbstractFastifyController,
31
32
 
33
+ //EVENTs
34
+ CrudEventEmitter,
35
+
32
36
 
33
37
  //Schemas
34
38
  IdParamSchema,