@solidxai/core 0.1.9 → 0.1.10-beta.2

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 (34) hide show
  1. package/dist/commands/run-tests.command.d.ts +2 -0
  2. package/dist/commands/run-tests.command.d.ts.map +1 -1
  3. package/dist/commands/run-tests.command.js +49 -17
  4. package/dist/commands/run-tests.command.js.map +1 -1
  5. package/dist/controllers/user.controller.d.ts.map +1 -1
  6. package/dist/controllers/user.controller.js.map +1 -1
  7. package/dist/services/crud.service.js.map +1 -1
  8. package/dist/services/field-metadata.service.js +2 -2
  9. package/dist/services/field-metadata.service.js.map +1 -1
  10. package/dist/services/queues/database-subscriber.service.js +3 -3
  11. package/dist/services/queues/database-subscriber.service.js.map +1 -1
  12. package/dist/services/queues/rabbitmq-subscriber.service.js +4 -4
  13. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  14. package/dist/services/queues/redis-subscriber.service.d.ts.map +1 -1
  15. package/dist/services/queues/redis-subscriber.service.js +4 -1
  16. package/dist/services/queues/redis-subscriber.service.js.map +1 -1
  17. package/dist/solid-core.module.d.ts +1 -0
  18. package/dist/solid-core.module.d.ts.map +1 -1
  19. package/dist/solid-core.module.js +1 -0
  20. package/dist/solid-core.module.js.map +1 -1
  21. package/dist/testing/reporter/webhook-reporter.d.ts +54 -0
  22. package/dist/testing/reporter/webhook-reporter.d.ts.map +1 -0
  23. package/dist/testing/reporter/webhook-reporter.js +74 -0
  24. package/dist/testing/reporter/webhook-reporter.js.map +1 -0
  25. package/package.json +1 -1
  26. package/src/commands/run-tests.command.ts +45 -17
  27. package/src/controllers/user.controller.ts +16 -16
  28. package/src/services/crud.service.ts +2 -2
  29. package/src/services/field-metadata.service.ts +2 -2
  30. package/src/services/queues/database-subscriber.service.ts +6 -6
  31. package/src/services/queues/rabbitmq-subscriber.service.ts +5 -5
  32. package/src/services/queues/redis-subscriber.service.ts +5 -2
  33. package/src/solid-core.module.ts +1 -0
  34. package/src/testing/reporter/webhook-reporter.ts +116 -0
@@ -20,37 +20,37 @@ export class UserController {
20
20
  @ApiBearerAuth("jwt")
21
21
  @Post()
22
22
  @UseInterceptors(AnyFilesInterceptor())
23
- create(@Body() createDto: CreateUserDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
24
- return this.service.create(createDto, files,solidRequestContext);
23
+ create(@Body() createDto: CreateUserDto, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
24
+ return this.service.create(createDto, files, solidRequestContext);
25
25
  }
26
26
 
27
27
  @ApiBearerAuth("jwt")
28
28
  @Post('/bulk')
29
29
  @UseInterceptors(AnyFilesInterceptor())
30
- insertMany(@Body() createDtos: CreateUserDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = [],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
31
- return this.service.insertMany(createDtos, filesArray,solidRequestContext);
30
+ insertMany(@Body() createDtos: CreateUserDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = [], @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
31
+ return this.service.insertMany(createDtos, filesArray, solidRequestContext);
32
32
  }
33
33
 
34
34
 
35
35
  @ApiBearerAuth("jwt")
36
36
  @Put(':id')
37
37
  @UseInterceptors(AnyFilesInterceptor())
38
- update(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File> ,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
39
- return this.service.update(id, updateDto, files,false,solidRequestContext);
38
+ update(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
39
+ return this.service.update(id, updateDto, files, false, solidRequestContext);
40
40
  }
41
41
 
42
42
 
43
43
  @ApiBearerAuth("jwt")
44
44
  @Patch(':id/update-user-and-roles')
45
- updateUser(@Param('id') id: number,@Body() updateDto: any, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
46
- return this.service.updateUser(id, updateDto, files,solidRequestContext);
45
+ updateUser(@Param('id') id: number, @Body() updateDto: any, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
46
+ return this.service.updateUser(id, updateDto, files, solidRequestContext);
47
47
  }
48
48
 
49
49
 
50
50
  @ApiBearerAuth("jwt")
51
51
  @Patch(':id')
52
52
  @UseInterceptors(AnyFilesInterceptor())
53
- partialUpdate(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
53
+ partialUpdate(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
54
54
  return this.service.update(id, updateDto, files, true, solidRequestContext);
55
55
  }
56
56
 
@@ -67,8 +67,8 @@ export class UserController {
67
67
  @ApiQuery({ name: 'populateMedia', required: false, type: Array })
68
68
  @ApiQuery({ name: 'filters', required: false, type: Array })
69
69
  @Get()
70
- async findMany(@Query() query: any, @SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
71
- return this.service.find(query,solidRequestContext);
70
+ async findMany(@Query() query: any, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
71
+ return this.service.find(query, solidRequestContext);
72
72
  }
73
73
 
74
74
  @ApiBearerAuth("jwt")
@@ -81,18 +81,18 @@ export class UserController {
81
81
 
82
82
  @ApiBearerAuth("jwt")
83
83
  @Get(':id')
84
- async findOne(@Param('id') id: string, @Query() query: any,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
85
- return this.service.findOne(+id, query,solidRequestContext);
84
+ async findOne(@Param('id') id: string, @Query() query: any, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
85
+ return this.service.findOne(+id, query, solidRequestContext);
86
86
  }
87
87
 
88
88
  @Delete('/bulk')
89
- async deleteMany(@Body() ids: number[],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
90
- return this.service.deleteMany(ids,solidRequestContext);
89
+ async deleteMany(@Body() ids: number[], @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
90
+ return this.service.deleteMany(ids, solidRequestContext);
91
91
  }
92
92
 
93
93
  @ApiBearerAuth("jwt")
94
94
  @Delete(':id')
95
- async delete(@Param('id') id: number,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
95
+ async delete(@Param('id') id: number, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
96
96
  return this.service.delete(id, solidRequestContext);
97
97
  }
98
98
 
@@ -920,7 +920,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
920
920
  );
921
921
 
922
922
  return { message: SUCCESS_MESSAGES.RECORD_RECOVERED, data: softDeletedRows };
923
- } catch (error) {
923
+ } catch (error: any) {
924
924
  if (error instanceof QueryFailedError) {
925
925
  if ((error as any).code === '23505') {
926
926
  throw new Error(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
@@ -966,7 +966,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
966
966
  );
967
967
 
968
968
  return { message: SUCCESS_MESSAGES.SELECTED_RECORDS_RECOVERED, recoveredIds: ids };
969
- } catch (error) {
969
+ } catch (error: any) {
970
970
  if (error instanceof QueryFailedError) {
971
971
  if ((error as any).code === "23505") {
972
972
  throw new Error(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
@@ -745,6 +745,8 @@ export class FieldMetadataService implements OnApplicationBootstrap {
745
745
  "type",
746
746
  "ormType",
747
747
  "isSystem",
748
+ "regexPattern",
749
+ "regexPatternNotMatchingErrorMsg",
748
750
  "defaultValue",
749
751
  "min",
750
752
  "max",
@@ -772,8 +774,6 @@ export class FieldMetadataService implements OnApplicationBootstrap {
772
774
  "regexPattern",
773
775
  "regexPatternNotMatchingErrorMsg",
774
776
  "defaultValue",
775
- "min",
776
- "max",
777
777
  "required",
778
778
  "unique",
779
779
  "index",
@@ -16,9 +16,9 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
16
16
  protected readonly mqMessageQueueService: MqMessageQueueService,
17
17
  protected readonly poller: PollerService,
18
18
  ) {
19
- this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
20
- if (!this.serviceRole) {
21
- this.logger.debug('Queue service Role is not defined in the environment variables');
19
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
20
+ if (!process.env.QUEUES_SERVICE_ROLE) {
21
+ this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting DatabaseSubscriber service role to "both".');
22
22
  }
23
23
  // this.logger.debug(`DatabaseSubscriber instance created with options: ${JSON.stringify(this.options())}`);
24
24
  }
@@ -60,7 +60,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
60
60
 
61
61
  await this.processMessage(message);
62
62
  }
63
- catch (error) {
63
+ catch (error: any) {
64
64
  this.logger.error(`Error processing message: ${error.message}`);
65
65
 
66
66
  // if an error occurs then if retryCount is set we start retrying.
@@ -152,7 +152,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
152
152
  private async retryMessage(message: QueueMessage<T>) {
153
153
  try {
154
154
  await this.processMessage(message);
155
- } catch (error) {
155
+ } catch (error: any) {
156
156
  if (message.currentRetry < message.retryCount) {
157
157
  await this.updateStatusInDatabase('retrying', message);
158
158
 
@@ -203,7 +203,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
203
203
  this.logger.debug(`Message status updated to ${stage} for messageId: ${mqMessage.id}`);
204
204
  }
205
205
  }
206
- catch (error) {
206
+ catch (error: any) {
207
207
  this.logger.error(error.message, error.stack);
208
208
  }
209
209
  }
@@ -31,12 +31,12 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
31
31
 
32
32
  constructor(protected readonly mqMessageService: MqMessageService, protected readonly mqMessageQueueService: MqMessageQueueService) {
33
33
  this.url = process.env.QUEUES_RABBIT_MQ_URL;
34
- this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
34
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
35
35
  if (!this.url) {
36
36
  this.logger.debug('RabbitMqPublisher url is not defined in the environment variables');
37
37
  }
38
- if (!this.serviceRole) {
39
- this.logger.debug('Queue service Role is not defined in the environment variables');
38
+ if (!process.env.QUEUES_SERVICE_ROLE) {
39
+ this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting RabbitMqSubscriber service role to "both".');
40
40
  }
41
41
  // this.logger.debug(`RabbitMqSubscriber instance created with options: ${JSON.stringify(this.options())} and url: ${this.url}`);
42
42
  }
@@ -85,7 +85,7 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
85
85
  async onModuleInit(): Promise<void> {
86
86
  // Not using SettingService here as that will necessitate all implementors of RabbitMqSubscriber to also inject SettingService which is not ideal.
87
87
  // Instead we directly read the environment variables here.
88
- const defaultBroker = process.env.QUEUES_DEFAULT_BROKER || 'rabbitmq';
88
+ const defaultBroker = process.env.QUEUES_DEFAULT_BROKER || 'database';
89
89
  const solidCliRunning = process.env.SOLID_CLI_RUNNING || "false";
90
90
  const queueNameRegex = (process.env.QUEUES_QUEUE_NAME_REGEX_TO_ENABLE || '').trim();
91
91
  const roleAllowed = ['both', 'subscriber'].includes(this.serviceRole);
@@ -407,7 +407,7 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
407
407
  await this.mqMessageService.repo.update(mqMessage.id, updatedFields);
408
408
  }
409
409
  }
410
- catch (error) {
410
+ catch (error: any) {
411
411
  this.logger.error(error.message, error.stack);
412
412
  }
413
413
 
@@ -17,7 +17,10 @@ export abstract class RedisSubscriber<T> implements OnModuleInit, OnModuleDestro
17
17
  protected readonly mqMessageService: MqMessageService,
18
18
  protected readonly mqMessageQueueService: MqMessageQueueService,
19
19
  ) {
20
- this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
20
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
21
+ if (!process.env.QUEUES_SERVICE_ROLE) {
22
+ this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting RedisSubscriber service role to "both".');
23
+ }
21
24
  if (!process.env.QUEUES_REDIS_URL) {
22
25
  this.logger.debug('RedisSubscriber: QUEUES_REDIS_URL is not defined in the environment variables');
23
26
  }
@@ -201,7 +204,7 @@ export abstract class RedisSubscriber<T> implements OnModuleInit, OnModuleDestro
201
204
  if (stage === 'failed') updatedFields['error'] = error;
202
205
  await this.mqMessageService.repo.update(mqMessage.id, updatedFields);
203
206
  }
204
- } catch (err) {
207
+ } catch (err: any) {
205
208
  this.logger.error(err.message, err.stack);
206
209
  }
207
210
  }
@@ -1,3 +1,4 @@
1
+ import 'multer';
1
2
  import { Global, MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
2
3
  import * as express from 'express';
3
4
  import { ConfigModule, ConfigService } from '@nestjs/config';
@@ -0,0 +1,116 @@
1
+ import { ConsoleReporter } from "./console-reporter";
2
+ import type { OpStep, ScenarioSpec } from "../contracts/testing-metadata.types";
3
+ import type { StepPhase } from "../contracts/runtime-context.types";
4
+
5
+ interface TestStepResult {
6
+ phase: string;
7
+ operation: string;
8
+ name?: string;
9
+ ok: boolean;
10
+ durationMs: number;
11
+ error?: string;
12
+ }
13
+
14
+ interface TestScenarioResult {
15
+ id: string;
16
+ name?: string;
17
+ ok: boolean;
18
+ durationMs: number;
19
+ error?: string;
20
+ steps: TestStepResult[];
21
+ }
22
+
23
+ export interface TestRunPayload {
24
+ runName: string;
25
+ startedAt: string;
26
+ completedAt: string;
27
+ durationMs: number;
28
+ exitCode: number;
29
+ total: number;
30
+ passed: number;
31
+ failed: number;
32
+ scenarios: TestScenarioResult[];
33
+ }
34
+
35
+ function formatError(err: unknown): string {
36
+ if (!err) return "";
37
+ if (err instanceof Error) return err.stack || err.message;
38
+ return String(err);
39
+ }
40
+
41
+ export class WebhookReporter extends ConsoleReporter {
42
+ private readonly startedAt = new Date();
43
+ private readonly accumulated: TestScenarioResult[] = [];
44
+ private currentSteps: TestStepResult[] = [];
45
+
46
+ constructor(
47
+ private readonly webhookUrl: string,
48
+ private readonly runName: string,
49
+ ) {
50
+ super();
51
+ }
52
+
53
+ override onStepEnd(args: {
54
+ scenarioId: string;
55
+ phase: StepPhase;
56
+ step: OpStep;
57
+ ok: boolean;
58
+ error?: unknown;
59
+ durationMs: number;
60
+ }): void {
61
+ super.onStepEnd(args);
62
+ this.currentSteps.push({
63
+ phase: String(args.phase),
64
+ operation: args.step.op,
65
+ name: args.step.name,
66
+ ok: args.ok,
67
+ durationMs: args.durationMs,
68
+ error: args.error ? formatError(args.error) : undefined,
69
+ });
70
+ }
71
+
72
+ override onScenarioEnd(
73
+ scenario: ScenarioSpec,
74
+ result: { ok: boolean; error?: unknown; durationMs: number },
75
+ ): void {
76
+ super.onScenarioEnd(scenario, result);
77
+ this.accumulated.push({
78
+ id: scenario.id,
79
+ name: scenario.name,
80
+ ok: result.ok,
81
+ durationMs: result.durationMs,
82
+ error: result.error ? formatError(result.error) : undefined,
83
+ steps: [...this.currentSteps],
84
+ });
85
+ this.currentSteps = [];
86
+ }
87
+
88
+ async flush(exitCode: number): Promise<void> {
89
+ const completedAt = new Date();
90
+ const payload: TestRunPayload = {
91
+ runName: this.runName,
92
+ startedAt: this.startedAt.toISOString(),
93
+ completedAt: completedAt.toISOString(),
94
+ durationMs: completedAt.getTime() - this.startedAt.getTime(),
95
+ exitCode,
96
+ total: this.accumulated.length,
97
+ passed: this.accumulated.filter((s) => s.ok).length,
98
+ failed: this.accumulated.filter((s) => !s.ok).length,
99
+ scenarios: this.accumulated,
100
+ };
101
+
102
+ try {
103
+ const response = await fetch(this.webhookUrl, {
104
+ method: "POST",
105
+ headers: { "Content-Type": "application/json" },
106
+ body: JSON.stringify(payload),
107
+ signal: AbortSignal.timeout(10_000),
108
+ });
109
+ if (!response.ok) {
110
+ console.warn(`[WebhookReporter] Webhook returned ${response.status}`);
111
+ }
112
+ } catch (err) {
113
+ console.warn(`[WebhookReporter] Failed to deliver test results: ${err}`);
114
+ }
115
+ }
116
+ }