@solidxai/core 0.1.6-beta.23 → 0.1.6-beta.24

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 (45) hide show
  1. package/dist/helpers/bootstrap.helper.js +1 -1
  2. package/dist/helpers/bootstrap.helper.js.map +1 -1
  3. package/dist/interfaces.d.ts +1 -0
  4. package/dist/interfaces.d.ts.map +1 -1
  5. package/dist/interfaces.js.map +1 -1
  6. package/dist/jobs/computed-field-evaluation-queue-options.d.ts +1 -0
  7. package/dist/jobs/computed-field-evaluation-queue-options.d.ts.map +1 -1
  8. package/dist/jobs/computed-field-evaluation-queue-options.js +1 -0
  9. package/dist/jobs/computed-field-evaluation-queue-options.js.map +1 -1
  10. package/dist/services/queues/rabbitmq-publisher.service.d.ts +1 -0
  11. package/dist/services/queues/rabbitmq-publisher.service.d.ts.map +1 -1
  12. package/dist/services/queues/rabbitmq-publisher.service.js +6 -1
  13. package/dist/services/queues/rabbitmq-publisher.service.js.map +1 -1
  14. package/dist/services/queues/rabbitmq-subscriber.service.d.ts +1 -0
  15. package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -1
  16. package/dist/services/queues/rabbitmq-subscriber.service.js +15 -4
  17. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  18. package/dist/services/request-context.service.d.ts +2 -1
  19. package/dist/services/request-context.service.d.ts.map +1 -1
  20. package/dist/services/request-context.service.js.map +1 -1
  21. package/dist/subscribers/created-by-updated-by.subscriber.d.ts +0 -1
  22. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
  23. package/dist/subscribers/created-by-updated-by.subscriber.js +3 -13
  24. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
  25. package/dist-tests/api/authenticate.spec.js +119 -0
  26. package/dist-tests/api/authenticate.spec.js.map +1 -0
  27. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
  28. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
  29. package/dist-tests/api/ping.spec.js +21 -0
  30. package/dist-tests/api/ping.spec.js.map +1 -0
  31. package/dist-tests/helpers/auth.js +41 -0
  32. package/dist-tests/helpers/auth.js.map +1 -0
  33. package/dist-tests/helpers/env.js +11 -0
  34. package/dist-tests/helpers/env.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/helpers/bootstrap.helper.ts +1 -1
  37. package/src/interfaces.ts +1 -0
  38. package/src/jobs/computed-field-evaluation-queue-options.ts +1 -0
  39. package/src/services/queues/rabbitmq-publisher.service.ts +8 -2
  40. package/src/services/queues/rabbitmq-subscriber.service.ts +16 -5
  41. package/src/services/request-context.service.ts +2 -1
  42. package/src/subscribers/created-by-updated-by.subscriber.ts +22 -16
  43. package/.claude/settings.local.json +0 -15
  44. package/logs_load_testing +0 -49
  45. package/src/services/1.js +0 -6
@@ -1,9 +1,10 @@
1
1
  import { ClsService } from "nestjs-cls";
2
2
  import { BasicFilterDto } from "src/dtos/basic-filters.dto";
3
+ import { ActiveUserData } from "src/interfaces/active-user-data.interface";
3
4
  export declare class RequestContextService {
4
5
  private readonly cls;
5
6
  constructor(cls: ClsService);
6
- getActiveUser(): any;
7
+ getActiveUser(): ActiveUserData | undefined;
7
8
  getIp(): string | undefined;
8
9
  getUserAgent(): string | undefined;
9
10
  setRequestFilter(filter: BasicFilterDto): void;
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.service.d.ts","sourceRoot":"","sources":["../../src/services/request-context.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,qBACa,qBAAqB;IAClB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,UAAU;IAI5C,aAAa;IAIb,KAAK,IAAI,MAAM,GAAG,SAAS;IAI3B,YAAY,IAAI,MAAM,GAAG,SAAS;IAIlC,gBAAgB,CAAC,MAAM,EAAE,cAAc;IAIvC,gBAAgB,IAAI,cAAc,GAAG,SAAS;CAIjD"}
1
+ {"version":3,"file":"request-context.service.d.ts","sourceRoot":"","sources":["../../src/services/request-context.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAE3E,qBACa,qBAAqB;IAClB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,UAAU;IAI5C,aAAa,IAAI,cAAc,GAAG,SAAS;IAI3C,KAAK,IAAI,MAAM,GAAG,SAAS;IAI3B,YAAY,IAAI,MAAM,GAAG,SAAS;IAIlC,gBAAgB,CAAC,MAAM,EAAE,cAAc;IAIvC,gBAAgB,IAAI,cAAc,GAAG,SAAS;CAIjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.service.js","sourceRoot":"","sources":["../../src/services/request-context.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAAwC;AACxC,4CAAiD;AAI1C,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAC9B,YAA6B,GAAe;QAAf,QAAG,GAAH,GAAG,CAAY;IAC5C,CAAC;IAGD,aAAa;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,4BAAgB,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,YAAY;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,gBAAgB,CAAC,MAAsB;QACnC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,gBAAgB;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;CAEJ,CAAA;AAzBY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAEyB,uBAAU;GADnC,qBAAqB,CAyBjC","sourcesContent":["import { Injectable } from \"@nestjs/common\";\nimport { ClsService } from \"nestjs-cls\";\nimport { REQUEST_USER_KEY } from \"src/constants\";\nimport { BasicFilterDto } from \"src/dtos/basic-filters.dto\";\n\n@Injectable()\nexport class RequestContextService {\n constructor(private readonly cls: ClsService) {\n }\n\n // This method i.e getActiveUser() will fetch the user from the request object in the context\n getActiveUser() {\n return this.cls.get(REQUEST_USER_KEY);\n }\n\n getIp(): string | undefined {\n return this.cls.get('ipAddress');\n }\n\n getUserAgent(): string | undefined {\n return this.cls.get('userAgent');\n }\n\n setRequestFilter(filter: BasicFilterDto) {\n this.cls.set('filter', filter);\n }\n\n getRequestFilter(): BasicFilterDto | undefined {\n return this.cls.get('filter');\n }\n\n}"]}
1
+ {"version":3,"file":"request-context.service.js","sourceRoot":"","sources":["../../src/services/request-context.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAAwC;AACxC,4CAAiD;AAK1C,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAC9B,YAA6B,GAAe;QAAf,QAAG,GAAH,GAAG,CAAY;IAC5C,CAAC;IAGD,aAAa;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,4BAAgB,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,YAAY;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,gBAAgB,CAAC,MAAsB;QACnC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,gBAAgB;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;CAEJ,CAAA;AAzBY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAEyB,uBAAU;GADnC,qBAAqB,CAyBjC","sourcesContent":["import { Injectable } from \"@nestjs/common\";\nimport { ClsService } from \"nestjs-cls\";\nimport { REQUEST_USER_KEY } from \"src/constants\";\nimport { BasicFilterDto } from \"src/dtos/basic-filters.dto\";\nimport { ActiveUserData } from \"src/interfaces/active-user-data.interface\";\n\n@Injectable()\nexport class RequestContextService {\n constructor(private readonly cls: ClsService) {\n }\n\n // This method i.e getActiveUser() will fetch the user from the request object in the context\n getActiveUser(): ActiveUserData | undefined {\n return this.cls.get(REQUEST_USER_KEY);\n }\n\n getIp(): string | undefined {\n return this.cls.get('ipAddress');\n }\n\n getUserAgent(): string | undefined {\n return this.cls.get('userAgent');\n }\n\n setRequestFilter(filter: BasicFilterDto) {\n this.cls.set('filter', filter);\n }\n\n getRequestFilter(): BasicFilterDto | undefined {\n return this.cls.get('filter');\n }\n\n}"]}
@@ -9,6 +9,5 @@ export declare class CreatedByUpdatedBySubscriber implements EntitySubscriberInt
9
9
  beforeInsert(event: InsertEvent<any>): Promise<void>;
10
10
  beforeUpdate(event: UpdateEvent<any>): Promise<void>;
11
11
  private stampUserField;
12
- private loadUser;
13
12
  }
14
13
  //# sourceMappingURL=created-by-updated-by.subscriber.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"created-by-updated-by.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAmB,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3G,qBAEa,4BAA6B,YAAW,yBAAyB;IAItE,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAJ1C,OAAO,CAAC,UAAU,CAAa;gBAGV,iBAAiB,EAAE,UAAU,EAC7B,qBAAqB,EAAE,qBAAqB;IAKjE,gBAAgB,CAAC,UAAU,EAAE,UAAU;IAKjC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;IAIpC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;YAI5B,cAAc;YAoBd,QAAQ;CAOzB"}
1
+ {"version":3,"file":"created-by-updated-by.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1F,qBAEa,4BAA6B,YAAW,yBAAyB;IAItE,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAJ1C,OAAO,CAAC,UAAU,CAAa;gBAGV,iBAAiB,EAAE,UAAU,EAC7B,qBAAqB,EAAE,qBAAqB;IAKjE,gBAAgB,CAAC,UAAU,EAAE,UAAU;IAKjC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;IAIpC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;YAI5B,cAAc;CAmC/B"}
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.CreatedByUpdatedBySubscriber = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const typeorm_1 = require("@nestjs/typeorm");
18
- const user_entity_1 = require("../entities/user.entity");
19
18
  const request_context_service_1 = require("../services/request-context.service");
20
19
  const typeorm_2 = require("typeorm");
21
20
  let CreatedByUpdatedBySubscriber = class CreatedByUpdatedBySubscriber {
@@ -41,23 +40,14 @@ let CreatedByUpdatedBySubscriber = class CreatedByUpdatedBySubscriber {
41
40
  if (!activeUserOrUndefined) {
42
41
  return;
43
42
  }
44
- const loadedUser = await this.loadUser(activeUserOrUndefined);
45
43
  if (isInsert) {
46
- event.entity.createdBy = loadedUser?.id;
47
- event.entity.updatedBy = loadedUser?.id;
44
+ event.entity.createdBy = activeUserOrUndefined?.sub;
45
+ event.entity.updatedBy = activeUserOrUndefined?.sub;
48
46
  }
49
47
  else {
50
- event.entity.updatedBy = loadedUser?.id;
48
+ event.entity.updatedBy = activeUserOrUndefined?.sub;
51
49
  }
52
50
  }
53
- async loadUser(activeUser) {
54
- const userRepo = this.defaultDataSource.getRepository(user_entity_1.User);
55
- const loadedUser = await userRepo.findOne({
56
- where: { id: activeUser.sub },
57
- });
58
- return loadedUser;
59
- ;
60
- }
61
51
  };
62
52
  exports.CreatedByUpdatedBySubscriber = CreatedByUpdatedBySubscriber;
63
53
  exports.CreatedByUpdatedBySubscriber = CreatedByUpdatedBySubscriber = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"created-by-updated-by.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmD;AACnD,6CAAmD;AACnD,yDAAgD;AAEhD,iFAA6E;AAC7E,qCAA2G;AAIpG,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IAErC,YAEqB,iBAA6B,EAC7B,qBAA4C;QAD5C,sBAAiB,GAAjB,iBAAiB,CAAY;QAC7B,0BAAqB,GAArB,qBAAqB,CAAuB;IAGjE,CAAC;IAED,gBAAgB,CAAC,UAAsB;QACnC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAA0C,EAAE,QAAiB;QACtF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACX,CAAC;QAED,MAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,CAAC;QACzE,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAkD,CAAC,CAAC;QAC3F,IAAI,QAAQ,EAAE,CAAC;YACX,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,UAAU,EAAE,EAAE,CAAC;YACxC,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,UAAU,EAAE,EAAE,CAAC;QAC5C,CAAC;aACI,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,UAAU,EAAE,EAAE,CAAC;QAC5C,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,UAA0B;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,kBAAI,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;YACtC,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE;SAChC,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;QAAA,CAAC;IACvB,CAAC;CACJ,CAAA;AAlDY,oEAA4B;uCAA5B,4BAA4B;IAFxC,IAAA,mBAAU,EAAC,EAAC,KAAK,EAAE,cAAK,CAAC,SAAS,EAAC,CAAC;IAK5B,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACiB,oBAAU;QACN,+CAAqB;GALxD,4BAA4B,CAkDxC","sourcesContent":["import { Injectable, Scope } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport { User } from \"src/entities/user.entity\";\nimport { ActiveUserData } from \"src/interfaces/active-user-data.interface\";\nimport { RequestContextService } from \"src/services/request-context.service\";\nimport { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from \"typeorm\";\n\n@Injectable({scope: Scope.TRANSIENT})\n// @EventSubscriber()\nexport class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {\n private dataSource: DataSource;\n constructor(\n @InjectDataSource()\n private readonly defaultDataSource: DataSource,\n private readonly requestContextService: RequestContextService,\n ) {\n // this.dataSource.subscribers.push(this);\n }\n\n bindToDataSource(dataSource: DataSource) {\n this.dataSource = dataSource;\n this.dataSource.subscribers.push(this);\n }\n\n async beforeInsert(event: InsertEvent<any>) {\n await this.stampUserField(event, true);\n }\n\n async beforeUpdate(event: UpdateEvent<any>) {\n await this.stampUserField(event, false);\n }\n\n private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean){\n if (!event.entity) {\n return;\n }\n // Get the current active user details from the request context\n const activeUserOrUndefined = this.requestContextService.getActiveUser();\n if (!activeUserOrUndefined) {\n return;\n }\n\n const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);\n if (isInsert) {\n event.entity.createdBy = loadedUser?.id;\n event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user\n }\n else {\n event.entity.updatedBy = loadedUser?.id;\n }\n }\n\n private async loadUser(activeUser: ActiveUserData): Promise<User> {\n const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application\n const loadedUser = await userRepo.findOne({\n where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token\n });\n return loadedUser;;\n }\n}"]}
1
+ {"version":3,"file":"created-by-updated-by.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmD;AACnD,6CAAmD;AACnD,iFAA6E;AAC7E,qCAA0F;AAInF,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IAErC,YAEqB,iBAA6B,EAC7B,qBAA4C;QAD5C,sBAAiB,GAAjB,iBAAiB,CAAY;QAC7B,0BAAqB,GAArB,qBAAqB,CAAuB;IAGjE,CAAC;IAED,gBAAgB,CAAC,UAAsB;QACnC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAA0C,EAAE,QAAiB;QACtF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACX,CAAC;QAED,MAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,CAAC;QACzE,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QAWD,IAAI,QAAQ,EAAE,CAAC;YACX,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,qBAAqB,EAAE,GAAG,CAAC;YACpD,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,qBAAqB,EAAE,GAAG,CAAC;QACxD,CAAC;aACI,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,qBAAqB,EAAE,GAAG,CAAC;QACxD,CAAC;IACL,CAAC;CASJ,CAAA;AA1DY,oEAA4B;uCAA5B,4BAA4B;IAFxC,IAAA,mBAAU,EAAC,EAAE,KAAK,EAAE,cAAK,CAAC,SAAS,EAAE,CAAC;IAK9B,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACiB,oBAAU;QACN,+CAAqB;GALxD,4BAA4B,CA0DxC","sourcesContent":["import { Injectable, Scope } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport { RequestContextService } from \"src/services/request-context.service\";\nimport { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from \"typeorm\";\n\n@Injectable({ scope: Scope.TRANSIENT })\n// @EventSubscriber()\nexport class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {\n private dataSource: DataSource;\n constructor(\n @InjectDataSource()\n private readonly defaultDataSource: DataSource,\n private readonly requestContextService: RequestContextService,\n ) {\n // this.dataSource.subscribers.push(this);\n }\n\n bindToDataSource(dataSource: DataSource) {\n this.dataSource = dataSource;\n this.dataSource.subscribers.push(this);\n }\n\n async beforeInsert(event: InsertEvent<any>) {\n await this.stampUserField(event, true);\n }\n\n async beforeUpdate(event: UpdateEvent<any>) {\n await this.stampUserField(event, false);\n }\n\n private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean) {\n if (!event.entity) {\n return;\n }\n // Get the current active user details from the request context\n const activeUserOrUndefined = this.requestContextService.getActiveUser();\n if (!activeUserOrUndefined) {\n return;\n }\n\n // const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);\n // if (isInsert) {\n // event.entity.createdBy = loadedUser?.id;\n // event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user\n // }\n // else {\n // event.entity.updatedBy = loadedUser?.id;\n // }\n\n if (isInsert) {\n event.entity.createdBy = activeUserOrUndefined?.sub;\n event.entity.updatedBy = activeUserOrUndefined?.sub; // For insert, we set both createdBy and updatedBy to the same user\n }\n else {\n event.entity.updatedBy = activeUserOrUndefined?.sub;\n }\n }\n\n // private async loadUser(activeUser: ActiveUserData): Promise<User> {\n // const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application\n // const loadedUser = await userRepo.findOne({\n // where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token\n // });\n // return loadedUser;;\n // }\n}"]}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const env_1 = require("../helpers/env");
5
+ const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
+ const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
7
+ const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
8
+ function base64UrlDecode(input) {
9
+ const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
10
+ const padded = normalized.length % 4 === 0
11
+ ? normalized
12
+ : normalized.padEnd(normalized.length + (4 - (normalized.length % 4)), "=");
13
+ return Buffer.from(padded, "base64").toString("utf-8");
14
+ }
15
+ function validateJwt(token) {
16
+ const parts = token.split(".");
17
+ if (parts.length !== 3) {
18
+ throw new Error("JWT must have three dot-separated parts.");
19
+ }
20
+ const headerJson = JSON.parse(base64UrlDecode(parts[0]));
21
+ const payloadJson = JSON.parse(base64UrlDecode(parts[1]));
22
+ if (!headerJson || typeof headerJson !== "object") {
23
+ throw new Error("JWT header must be a JSON object.");
24
+ }
25
+ if (!payloadJson || typeof payloadJson !== "object") {
26
+ throw new Error("JWT payload must be a JSON object.");
27
+ }
28
+ if (typeof payloadJson.exp !== "number") {
29
+ throw new Error("JWT payload.exp must be a number.");
30
+ }
31
+ return payloadJson;
32
+ }
33
+ (0, test_1.test)("API: authenticate succeeds with valid credentials", async () => {
34
+ const api = await test_1.request.newContext({
35
+ baseURL,
36
+ extraHTTPHeaders: {
37
+ accept: "*/*",
38
+ "content-type": "application/json",
39
+ },
40
+ });
41
+ try {
42
+ const res = await api.post("/api/iam/authenticate", {
43
+ data: {
44
+ email: TEST_USER_EMAIL,
45
+ username: "",
46
+ password: TEST_USER_PASSWORD,
47
+ },
48
+ });
49
+ (0, test_1.expect)(res.status()).toBe(200);
50
+ const json = await res.json();
51
+ (0, test_1.expect)(json.statusCode).toBe(200);
52
+ (0, test_1.expect)(Array.isArray(json.message)).toBe(true);
53
+ (0, test_1.expect)(json.message.length).toBe(0);
54
+ (0, test_1.expect)(json.error).toBe("");
55
+ const user = json.data?.user;
56
+ (0, test_1.expect)(user, "Expected data.user to be an object.").toBeTruthy();
57
+ (0, test_1.expect)(typeof user).toBe("object");
58
+ const email = user?.email;
59
+ (0, test_1.expect)(typeof email).toBe("string");
60
+ if (email === TEST_USER_EMAIL) {
61
+ (0, test_1.expect)(email).toBe(TEST_USER_EMAIL);
62
+ }
63
+ else {
64
+ (0, test_1.expect)(email.length).toBeGreaterThan(0);
65
+ }
66
+ (0, test_1.expect)(typeof user?.mobile).toBe("string");
67
+ (0, test_1.expect)(typeof user?.username).toBe("string");
68
+ (0, test_1.expect)(typeof user?.forcePasswordChange).toBe("boolean");
69
+ (0, test_1.expect)(typeof user?.id).toBe("number");
70
+ const roles = user?.roles;
71
+ (0, test_1.expect)(Array.isArray(roles)).toBe(true);
72
+ if (Array.isArray(roles)) {
73
+ (0, test_1.expect)(roles.every((role) => typeof role === "string")).toBe(true);
74
+ (0, test_1.expect)(roles).toContain("Admin");
75
+ }
76
+ const accessToken = json.data?.accessToken;
77
+ const refreshToken = json.data?.refreshToken;
78
+ (0, test_1.expect)(typeof accessToken).toBe("string");
79
+ (0, test_1.expect)(typeof refreshToken).toBe("string");
80
+ const accessPayload = validateJwt(accessToken);
81
+ const refreshPayload = validateJwt(refreshToken);
82
+ (0, test_1.expect)(typeof accessPayload.exp).toBe("number");
83
+ (0, test_1.expect)(typeof refreshPayload.exp).toBe("number");
84
+ }
85
+ finally {
86
+ await api.dispose();
87
+ }
88
+ });
89
+ (0, test_1.test)("API: authenticate fails with wrong password", async () => {
90
+ const api = await test_1.request.newContext({
91
+ baseURL,
92
+ extraHTTPHeaders: {
93
+ accept: "*/*",
94
+ "content-type": "application/json",
95
+ },
96
+ });
97
+ try {
98
+ const res = await api.post("/api/iam/authenticate", {
99
+ data: {
100
+ email: TEST_USER_EMAIL,
101
+ username: "",
102
+ password: `${TEST_USER_PASSWORD}__wrong`,
103
+ },
104
+ });
105
+ (0, test_1.expect)(res.status()).toBe(401);
106
+ const json = await res.json();
107
+ (0, test_1.expect)(json.statusCode).toBe(401);
108
+ (0, test_1.expect)(json.statusCodeMessage).toBe("Unauthorized");
109
+ (0, test_1.expect)(json.message).toBe("Invalid credentials");
110
+ (0, test_1.expect)(json.error).toBe("Invalid credentials");
111
+ (0, test_1.expect)(json.data?.statusCode).toBe(401);
112
+ (0, test_1.expect)(json.data?.error).toBe("Unauthorized");
113
+ (0, test_1.expect)(json.data?.message).toBe("Invalid credentials");
114
+ }
115
+ finally {
116
+ await api.dispose();
117
+ }
118
+ });
119
+ //# sourceMappingURL=authenticate.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../../tests/api/authenticate.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getRequiredEnv } from \"../helpers/env\";\n\ntype JwtPayload = {\n exp?: number;\n [key: string]: unknown;\n};\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\nconst TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\nconst TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\nfunction base64UrlDecode(input: string): string {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded =\n normalized.length % 4 === 0\n ? normalized\n : normalized.padEnd(\n normalized.length + (4 - (normalized.length % 4)),\n \"=\"\n );\n return Buffer.from(padded, \"base64\").toString(\"utf-8\");\n}\n\nfunction validateJwt(token: string): JwtPayload {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"JWT must have three dot-separated parts.\");\n }\n\n const headerJson = JSON.parse(base64UrlDecode(parts[0]));\n const payloadJson = JSON.parse(base64UrlDecode(parts[1]));\n\n if (!headerJson || typeof headerJson !== \"object\") {\n throw new Error(\"JWT header must be a JSON object.\");\n }\n\n if (!payloadJson || typeof payloadJson !== \"object\") {\n throw new Error(\"JWT payload must be a JSON object.\");\n }\n\n if (typeof payloadJson.exp !== \"number\") {\n throw new Error(\"JWT payload.exp must be a number.\");\n }\n\n return payloadJson as JwtPayload;\n}\n\ntest(\"API: authenticate succeeds with valid credentials\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n expect(json.statusCode).toBe(200);\n expect(Array.isArray(json.message)).toBe(true);\n expect(json.message.length).toBe(0);\n expect(json.error).toBe(\"\");\n\n const user = json.data?.user as Record<string, unknown> | undefined;\n expect(user, \"Expected data.user to be an object.\").toBeTruthy();\n expect(typeof user).toBe(\"object\");\n\n const email = user?.email;\n expect(typeof email).toBe(\"string\");\n if (email === TEST_USER_EMAIL) {\n expect(email).toBe(TEST_USER_EMAIL);\n } else {\n // If your API returns a fixed system email, replace this with an exact match.\n expect((email as string).length).toBeGreaterThan(0);\n }\n\n expect(typeof user?.mobile).toBe(\"string\");\n expect(typeof user?.username).toBe(\"string\");\n expect(typeof user?.forcePasswordChange).toBe(\"boolean\");\n expect(typeof user?.id).toBe(\"number\");\n\n const roles = user?.roles;\n expect(Array.isArray(roles)).toBe(true);\n if (Array.isArray(roles)) {\n expect(roles.every((role) => typeof role === \"string\")).toBe(true);\n expect(roles).toContain(\"Admin\");\n }\n\n const accessToken = json.data?.accessToken;\n const refreshToken = json.data?.refreshToken;\n expect(typeof accessToken).toBe(\"string\");\n expect(typeof refreshToken).toBe(\"string\");\n\n const accessPayload = validateJwt(accessToken as string);\n const refreshPayload = validateJwt(refreshToken as string);\n\n expect(typeof accessPayload.exp).toBe(\"number\");\n expect(typeof refreshPayload.exp).toBe(\"number\");\n } finally {\n await api.dispose();\n }\n});\n\ntest(\"API: authenticate fails with wrong password\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: `${TEST_USER_PASSWORD}__wrong`,\n },\n });\n\n expect(res.status()).toBe(401);\n const json = await res.json();\n\n expect(json.statusCode).toBe(401);\n expect(json.statusCodeMessage).toBe(\"Unauthorized\");\n expect(json.message).toBe(\"Invalid credentials\");\n expect(json.error).toBe(\"Invalid credentials\");\n expect(json.data?.statusCode).toBe(401);\n expect(json.data?.error).toBe(\"Unauthorized\");\n expect(json.data?.message).toBe(\"Invalid credentials\");\n } finally {\n await api.dispose();\n }\n});\n"]}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const auth_1 = require("../helpers/auth");
5
+ const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
+ async function getCityByName(name) {
7
+ const api = await test_1.request.newContext({
8
+ baseURL,
9
+ extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
10
+ });
11
+ try {
12
+ const res = await api.get(`/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`);
13
+ (0, test_1.expect)(res.status()).toBe(200);
14
+ const json = await res.json();
15
+ const record = json?.data?.records?.[0];
16
+ (0, test_1.expect)(record, `Expected city '${name}' in testData`).toBeTruthy();
17
+ (0, test_1.expect)(record.id, "Expected record to have id").toBeTruthy();
18
+ return record;
19
+ }
20
+ finally {
21
+ await api.dispose();
22
+ }
23
+ }
24
+ test_1.test.describe("CRUDService.findOne (cityMaster)", () => {
25
+ (0, test_1.test)("returns a city by id", async () => {
26
+ const city = await getCityByName("Mumbai");
27
+ const api = await test_1.request.newContext({
28
+ baseURL,
29
+ extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
30
+ });
31
+ try {
32
+ const res = await api.get(`/api/city-master/${city.id}`);
33
+ (0, test_1.expect)(res.status()).toBe(200);
34
+ const json = await res.json();
35
+ (0, test_1.expect)(json?.data?.id).toBe(city.id);
36
+ (0, test_1.expect)(json?.data?.name).toBe("Mumbai");
37
+ (0, test_1.expect)(json?.data?.description).toBe("Mumbai city");
38
+ }
39
+ finally {
40
+ await api.dispose();
41
+ }
42
+ });
43
+ (0, test_1.test)("supports fields[] selection", async () => {
44
+ const city = await getCityByName("Pune");
45
+ const api = await test_1.request.newContext({
46
+ baseURL,
47
+ extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
48
+ });
49
+ try {
50
+ const res = await api.get(`/api/city-master/${city.id}?fields[]=id&fields[]=name`);
51
+ (0, test_1.expect)(res.status()).toBe(200);
52
+ const json = await res.json();
53
+ (0, test_1.expect)(json?.data?.id).toBe(city.id);
54
+ (0, test_1.expect)(json?.data?.name).toBe("Pune");
55
+ (0, test_1.expect)(json?.data?.description).toBeUndefined();
56
+ (0, test_1.expect)(json?.data?.state).toBeUndefined();
57
+ }
58
+ finally {
59
+ await api.dispose();
60
+ }
61
+ });
62
+ (0, test_1.test)("supports populate=state", async () => {
63
+ const city = await getCityByName("Bengaluru");
64
+ const api = await test_1.request.newContext({
65
+ baseURL,
66
+ extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
67
+ });
68
+ try {
69
+ const res = await api.get(`/api/city-master/${city.id}?populate=state`);
70
+ (0, test_1.expect)(res.status()).toBe(200);
71
+ const json = await res.json();
72
+ (0, test_1.expect)(json?.data?.name).toBe("Bengaluru");
73
+ (0, test_1.expect)(json?.data?.state).toBeTruthy();
74
+ (0, test_1.expect)(json?.data?.state?.name).toBe("Karnataka");
75
+ }
76
+ finally {
77
+ await api.dispose();
78
+ }
79
+ });
80
+ (0, test_1.test)("returns 404 for missing id", async () => {
81
+ const api = await test_1.request.newContext({
82
+ baseURL,
83
+ extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
84
+ });
85
+ try {
86
+ const res = await api.get(`/api/city-master/99999999`);
87
+ (0, test_1.expect)(res.status()).toBe(404);
88
+ const json = await res.json();
89
+ (0, test_1.expect)(String(json?.message)).toContain("cityMaster");
90
+ (0, test_1.expect)(String(json?.message)).toContain("not found");
91
+ }
92
+ finally {
93
+ await api.dispose();
94
+ }
95
+ });
96
+ });
97
+ //# sourceMappingURL=crud-service.findOne.cityMaster.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud-service.findOne.cityMaster.spec.js","sourceRoot":"","sources":["../../tests/api/crud-service.findOne.cityMaster.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getAuthHeaders } from \"../helpers/auth\";\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\n\nasync function getCityByName(name: string) {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`\n );\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n const record = json?.data?.records?.[0];\n expect(record, `Expected city '${name}' in testData`).toBeTruthy();\n expect(record.id, \"Expected record to have id\").toBeTruthy();\n\n return record;\n } finally {\n await api.dispose();\n }\n}\n\ntest.describe(\"CRUDService.findOne (cityMaster)\", () => {\n test(\"returns a city by id\", async () => {\n const city = await getCityByName(\"Mumbai\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Mumbai\");\n expect(json?.data?.description).toBe(\"Mumbai city\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports fields[] selection\", async () => {\n const city = await getCityByName(\"Pune\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master/${city.id}?fields[]=id&fields[]=name`\n );\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Pune\");\n expect(json?.data?.description).toBeUndefined();\n expect(json?.data?.state).toBeUndefined();\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports populate=state\", async () => {\n const city = await getCityByName(\"Bengaluru\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}?populate=state`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.name).toBe(\"Bengaluru\");\n expect(json?.data?.state).toBeTruthy();\n expect(json?.data?.state?.name).toBe(\"Karnataka\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"returns 404 for missing id\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/99999999`);\n expect(res.status()).toBe(404);\n\n const json = await res.json();\n expect(String(json?.message)).toContain(\"cityMaster\");\n expect(String(json?.message)).toContain(\"not found\");\n } finally {\n await api.dispose();\n }\n });\n});\n"]}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ (0, test_1.test)("GET /api/ping returns pong", async () => {
5
+ const baseURL = process.env.BASE_URL || "http://localhost:3000";
6
+ if (!baseURL) {
7
+ throw new Error("baseURL is not configured. Set API_BASE_URL or use the default.");
8
+ }
9
+ const api = await test_1.request.newContext({ baseURL });
10
+ const res = await api.get("/api/ping");
11
+ (0, test_1.expect)(res.status()).toBe(200);
12
+ const body = await res.json();
13
+ (0, test_1.expect)(body).toEqual({
14
+ statusCode: 200,
15
+ message: [],
16
+ error: "",
17
+ data: { pong: "v1.0.2" },
18
+ });
19
+ await api.dispose();
20
+ });
21
+ //# sourceMappingURL=ping.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ping.spec.js","sourceRoot":"","sources":["../../tests/api/ping.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AAEzD,IAAA,WAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEvC,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAA,aAAM,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACzB,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\n\ntest(\"GET /api/ping returns pong\", async () => {\n const baseURL = process.env.BASE_URL || \"http://localhost:3000\";\n if (!baseURL) {\n throw new Error(\"baseURL is not configured. Set API_BASE_URL or use the default.\");\n }\n const api = await request.newContext({ baseURL });\n\n const res = await api.get(\"/api/ping\");\n\n expect(res.status()).toBe(200);\n\n const body = await res.json();\n expect(body).toEqual({\n statusCode: 200,\n message: [],\n error: \"\",\n data: { pong: \"v1.0.2\" },\n });\n\n await api.dispose();\n});\n"]}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAccessToken = getAccessToken;
4
+ exports.getAuthHeaders = getAuthHeaders;
5
+ const test_1 = require("@playwright/test");
6
+ const env_1 = require("./env");
7
+ async function getAccessToken(baseURL) {
8
+ const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
9
+ const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
10
+ const api = await test_1.request.newContext({
11
+ baseURL,
12
+ extraHTTPHeaders: {
13
+ accept: "*/*",
14
+ "content-type": "application/json",
15
+ },
16
+ });
17
+ try {
18
+ const res = await api.post("/api/iam/authenticate", {
19
+ data: {
20
+ email: TEST_USER_EMAIL,
21
+ username: "",
22
+ password: TEST_USER_PASSWORD,
23
+ },
24
+ });
25
+ (0, test_1.expect)(res.status()).toBe(200);
26
+ const json = await res.json();
27
+ const token = json?.data?.accessToken;
28
+ (0, test_1.expect)(token, "Expected access token from authenticate endpoint.").toBeTruthy();
29
+ return token;
30
+ }
31
+ finally {
32
+ await api.dispose();
33
+ }
34
+ }
35
+ async function getAuthHeaders(baseURL) {
36
+ const token = await getAccessToken(baseURL);
37
+ return {
38
+ authorization: `Bearer ${token}`,
39
+ };
40
+ }
41
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../tests/helpers/auth.ts"],"names":[],"mappings":";;AAGA,wCA6BC;AAED,wCAKC;AAvCD,2CAAmD;AACnD,+BAAuC;AAEhC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,eAAe,GAAG,IAAA,oBAAc,EAAC,iBAAiB,CAAC,CAAC;IAC1D,MAAM,kBAAkB,GAAG,IAAA,oBAAc,EAAC,oBAAoB,CAAC,CAAC;IAEhE,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;QACnC,OAAO;QACP,gBAAgB,EAAE;YAChB,MAAM,EAAE,KAAK;YACb,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;YAClD,IAAI,EAAE;gBACJ,KAAK,EAAE,eAAe;gBACtB,QAAQ,EAAE,EAAE;gBACZ,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,WAAiC,CAAC;QAC5D,IAAA,aAAM,EAAC,KAAK,EAAE,mDAAmD,CAAC,CAAC,UAAU,EAAE,CAAC;QAChF,OAAO,KAAe,CAAC;IACzB,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO;QACL,aAAa,EAAE,UAAU,KAAK,EAAE;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { expect, request } from \"@playwright/test\";\nimport { getRequiredEnv } from \"./env\";\n\nexport async function getAccessToken(baseURL: string) {\n const TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\n const TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n const token = json?.data?.accessToken as string | undefined;\n expect(token, \"Expected access token from authenticate endpoint.\").toBeTruthy();\n return token as string;\n } finally {\n await api.dispose();\n }\n}\n\nexport async function getAuthHeaders(baseURL: string) {\n const token = await getAccessToken(baseURL);\n return {\n authorization: `Bearer ${token}`,\n };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRequiredEnv = getRequiredEnv;
4
+ function getRequiredEnv(name) {
5
+ const value = process.env[name];
6
+ if (!value) {
7
+ throw new Error(`Missing required env var: ${name}`);
8
+ }
9
+ return value;
10
+ }
11
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../tests/helpers/env.ts"],"names":[],"mappings":";;AAAA,wCAMC;AAND,SAAgB,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["export function getRequiredEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(`Missing required env var: ${name}`);\n }\n return value;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidxai/core",
3
- "version": "0.1.6-beta.23",
3
+ "version": "0.1.6-beta.24",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -110,7 +110,7 @@ export async function bootstrapSolidApp(
110
110
  if (req.query) {
111
111
  req.query = qs.parse(req.url.split('?')[1], {
112
112
  allowDots: true,
113
- depth: 10,
113
+ depth: 20,
114
114
  arrayLimit: 100,
115
115
  });
116
116
  }
package/src/interfaces.ts CHANGED
@@ -271,6 +271,7 @@ export interface QueuesModuleOptions {
271
271
  type: BrokerType;
272
272
  queueName: string;
273
273
  prefetch?: number;
274
+ persistToDatabase?: boolean;
274
275
  }
275
276
 
276
277
  export type MediaWithFullUrl = Media & {
@@ -7,4 +7,5 @@ export default {
7
7
  type: BrokerType.RabbitMQ,
8
8
  queueName: QUEUE_NAME,
9
9
  prefetch: 10,
10
+ persistToDatabase: false,
10
11
  };
@@ -34,6 +34,10 @@ export abstract class RabbitMqPublisher<T> implements OnModuleDestroy, QueuePubl
34
34
 
35
35
  abstract options(): QueuesModuleOptions;
36
36
 
37
+ protected shouldPersistToDatabase(): boolean {
38
+ return this.options().persistToDatabase ?? true;
39
+ }
40
+
37
41
  private async ensureConnectionAndChannel(): Promise<amqp.Channel> {
38
42
  if (this.channel) {
39
43
  return this.channel;
@@ -170,7 +174,9 @@ export abstract class RabbitMqPublisher<T> implements OnModuleDestroy, QueuePubl
170
174
  message.messageId = uuidv4();
171
175
 
172
176
  // Save the message to the DB so that we can then change its status in the subscriber...
173
- await this.persistToDatabase(namespacedQueueName, message);
177
+ if (this.shouldPersistToDatabase()) {
178
+ await this.persistToDatabase(namespacedQueueName, message);
179
+ }
174
180
 
175
181
  // wait for the channel to confirm
176
182
  try {
@@ -199,7 +205,7 @@ export abstract class RabbitMqPublisher<T> implements OnModuleDestroy, QueuePubl
199
205
 
200
206
  private async persistToDatabase(queueName: string, message: QueueMessage<T>) {
201
207
 
202
- // TODO: make an entry in the relevant database table, generate a unique id earlier.
208
+ // make an entry in the relevant database table, generate a unique id earlier.
203
209
  try {
204
210
  // 1. resolve the queue first
205
211
  const mqMessageQueue = await this.mqMessageQueueService.resolveQueue(queueName);
@@ -56,6 +56,10 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
56
56
 
57
57
  abstract options(): QueuesModuleOptions;
58
58
 
59
+ protected shouldPersistToDatabase(): boolean {
60
+ return this.options().persistToDatabase ?? true;
61
+ }
62
+
59
63
  async establishConnection(): Promise<amqp.Connection> {
60
64
 
61
65
  const url = new URL(this.url);
@@ -236,7 +240,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
236
240
  this.logger.error(`Error processing message on queue ${queueName}: ${errorMessage}`, (error as Error)?.stack);
237
241
 
238
242
  if (message.currentRetry < message.retryCount) {
239
- await this.updateStatusInDatabase('retrying', message);
243
+ if (this.shouldPersistToDatabase()) {
244
+ await this.updateStatusInDatabase('retrying', message);
245
+ }
240
246
 
241
247
  message.currentRetry++;
242
248
  const retryQueue = `${queueName}.retry`;
@@ -254,8 +260,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
254
260
  this.logger.warn(`Retrying message (${message.currentRetry}/${message.retryCount}) after ${message.retryInterval}ms on queue ${queueName}`);
255
261
  return;
256
262
  }
257
-
258
- await this.updateStatusInDatabase('failed', message, errorMessage, '');
263
+ if (this.shouldPersistToDatabase()) {
264
+ await this.updateStatusInDatabase('failed', message, errorMessage, '');
265
+ }
259
266
  channel.ack(rawMessage);
260
267
  await this.publishToFailedQueue(queueName, Buffer.from(JSON.stringify(message)), channel, error);
261
268
  this.logger.error(`Message failed after ${message.retryCount} attempts on queue ${queueName}: ${errorMessage}`);
@@ -355,7 +362,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
355
362
  * Abstract method for message processing logic.
356
363
  */
357
364
  protected async processMessage(message: QueueMessage<T>, rawMessage, channel, queueName: string): Promise<void> {
358
- await this.updateStatusInDatabase('started', message);
365
+ if (this.shouldPersistToDatabase()) {
366
+ await this.updateStatusInDatabase('started', message);
367
+ }
359
368
 
360
369
  // Capture the results of handling the task.
361
370
  const result = await this.subscribeWithTimeout(message, queueName);
@@ -364,7 +373,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
364
373
  channel.ack(rawMessage);
365
374
 
366
375
  // Persist success output and timing.
367
- await this.updateStatusInDatabase('succeeded', message, '', result ? JSON.stringify(result, null, 2) : '');
376
+ if (this.shouldPersistToDatabase()) {
377
+ await this.updateStatusInDatabase('succeeded', message, '', result ? JSON.stringify(result, null, 2) : '');
378
+ }
368
379
 
369
380
  }
370
381
 
@@ -2,6 +2,7 @@ import { Injectable } from "@nestjs/common";
2
2
  import { ClsService } from "nestjs-cls";
3
3
  import { REQUEST_USER_KEY } from "src/constants";
4
4
  import { BasicFilterDto } from "src/dtos/basic-filters.dto";
5
+ import { ActiveUserData } from "src/interfaces/active-user-data.interface";
5
6
 
6
7
  @Injectable()
7
8
  export class RequestContextService {
@@ -9,7 +10,7 @@ export class RequestContextService {
9
10
  }
10
11
 
11
12
  // This method i.e getActiveUser() will fetch the user from the request object in the context
12
- getActiveUser() {
13
+ getActiveUser(): ActiveUserData | undefined {
13
14
  return this.cls.get(REQUEST_USER_KEY);
14
15
  }
15
16