@solidxai/core 0.1.17 → 0.1.18-beta.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.
- package/CHANGELOG.md +672 -0
- package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.d.ts +2 -0
- package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.js +31 -7
- package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +7 -3
- package/dist/services/crud.service.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +11 -6
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/helpers/field-crud-managers/SelectionStaticFieldCrudManager.ts +32 -7
- package/src/index.ts +1 -0
- package/src/services/crud.service.ts +11 -3
- package/src/subscribers/audit.subscriber.ts +15 -8
|
@@ -26,7 +26,7 @@ export class SelectionStaticFieldCrudManager implements FieldCrudManager {
|
|
|
26
26
|
return [
|
|
27
27
|
{
|
|
28
28
|
field: this.options.fieldName,
|
|
29
|
-
error: `Field: ${this.options.fieldName} must be a valid array`,
|
|
29
|
+
error: `Field: ${this.options.fieldName} must be a valid string representation of a JSON array of selection values. Allowed values: '${this.formatAllowedValues()}'. Received: ${this.formatValue(fieldValue)}`,
|
|
30
30
|
},
|
|
31
31
|
];
|
|
32
32
|
}
|
|
@@ -43,7 +43,7 @@ export class SelectionStaticFieldCrudManager implements FieldCrudManager {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// Apply validations to each value
|
|
46
|
-
const allErrors = await Promise.all(values.map((val) => this.applyValidations(val)));
|
|
46
|
+
const allErrors = await Promise.all(values.map((val) => this.applyValidations(val, values)));
|
|
47
47
|
return allErrors.flat();
|
|
48
48
|
} else {
|
|
49
49
|
// For non-multi-select, apply validations to the single field value
|
|
@@ -69,22 +69,47 @@ export class SelectionStaticFieldCrudManager implements FieldCrudManager {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
private applyValidations(fieldValue: any): ValidationError[] {
|
|
72
|
+
private applyValidations(fieldValue: any, receivedValue: any = fieldValue): ValidationError[] {
|
|
73
73
|
const errors: ValidationError[] = [];
|
|
74
74
|
this.isApplyRequiredValidation() && isEmpty(fieldValue) ? errors.push({ field: this.options.fieldName, error: `Field: ${this.options.fieldName} is required` }) : "no errors";
|
|
75
75
|
if (isNotEmpty(fieldValue)) {
|
|
76
|
-
errors.push(...this.applyFormatValidations(fieldValue));
|
|
76
|
+
errors.push(...this.applyFormatValidations(fieldValue, receivedValue));
|
|
77
77
|
}
|
|
78
78
|
return errors;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
private applyFormatValidations(fieldValue: any): ValidationError[] {
|
|
81
|
+
private applyFormatValidations(fieldValue: any, receivedValue: any = fieldValue): ValidationError[] {
|
|
82
82
|
const errors: ValidationError[] = [];
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
const allowedValues = this.formatAllowedValues();
|
|
84
|
+
const received = this.formatValue(receivedValue);
|
|
85
|
+
!this.isValidSelectionValueType(fieldValue, this.options.selectionValueType) ? errors.push({
|
|
86
|
+
field: this.options.fieldName,
|
|
87
|
+
error: `Field: ${this.options.fieldName} has an invalid selection value type. Expected ${this.options.selectionValueType}. Allowed values: ${allowedValues}. Received: ${received}`,
|
|
88
|
+
}) : "no errors";
|
|
89
|
+
!this.isValidSelectionStaticValue(fieldValue, this.options.selectionValueType, this.options.selectionStaticValues) ? errors.push({
|
|
90
|
+
field: this.options.fieldName,
|
|
91
|
+
error: `Field: ${this.options.fieldName} contains an invalid selection value. Allowed values: ${allowedValues}. Received: ${received}`,
|
|
92
|
+
}) : "no errors";
|
|
85
93
|
return errors;
|
|
86
94
|
}
|
|
87
95
|
|
|
96
|
+
private formatAllowedValues(): string {
|
|
97
|
+
const values = this.options.selectionStaticValues.map((value) => {
|
|
98
|
+
const rawValue = value.split(":")[0];
|
|
99
|
+
return this.options.selectionValueType === SelectionValueType.int ? Number(rawValue) : rawValue;
|
|
100
|
+
});
|
|
101
|
+
return this.formatValue(values);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private formatValue(value: any): string {
|
|
105
|
+
try {
|
|
106
|
+
const formatted = JSON.stringify(value);
|
|
107
|
+
return formatted === undefined ? String(value) : formatted;
|
|
108
|
+
} catch {
|
|
109
|
+
return String(value);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
88
113
|
transformForCreate(dto: any): any {
|
|
89
114
|
return dto;
|
|
90
115
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export * from './decorators/security-rule-config-provider.decorator'
|
|
|
29
29
|
export * from './decorators/sms-provider.decorator'
|
|
30
30
|
export * from './decorators/settings-provider.decorator'
|
|
31
31
|
export * from './decorators/extension-user-creation-provider.decorator'
|
|
32
|
+
export * from './decorators/whatsapp-provider.decorator'
|
|
32
33
|
export * from './decorators/workflow-node-provider.decorator'
|
|
33
34
|
export * from './decorators/workflow-field-data-provider.decorator'
|
|
34
35
|
|
|
@@ -300,15 +300,23 @@ private async prepareManyToManyAuditSnapshot(entity: T,id: number,modelSingularN
|
|
|
300
300
|
field.relationType !== 'one-to-many'
|
|
301
301
|
);
|
|
302
302
|
if (auditRelationFields.length > 0) {
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
// Fetch each audit-tracked relation independently rather than joining all of
|
|
304
|
+
// them into a single query: for an entity with several many-to-many
|
|
305
|
+
// relations, one combined query multiplies row counts across every joined
|
|
306
|
+
// relation at once and can blow past the statement timeout as data grows.
|
|
305
307
|
const auditBeforeEntity = await this.repo.findOne({
|
|
306
308
|
where: {
|
|
307
309
|
id: id,
|
|
308
310
|
} as unknown as FindOptionsWhere<T>,
|
|
309
|
-
relations: relations as any,
|
|
310
311
|
});
|
|
311
312
|
if (auditBeforeEntity) {
|
|
313
|
+
for (const field of auditRelationFields) {
|
|
314
|
+
(auditBeforeEntity as any)[field.name] = await this.repo.manager
|
|
315
|
+
.createQueryBuilder()
|
|
316
|
+
.relation(this.repo.target, field.name)
|
|
317
|
+
.of(id)
|
|
318
|
+
.loadMany();
|
|
319
|
+
}
|
|
312
320
|
Object.defineProperty(entity, AUDIT_BEFORE_SNAPSHOT, {
|
|
313
321
|
configurable: true,
|
|
314
322
|
enumerable: false,
|
|
@@ -98,19 +98,26 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
const relations: Record<string, boolean> = {};
|
|
102
|
-
|
|
103
|
-
auditRelationFields.forEach(field => {
|
|
104
|
-
relations[field.name] = true;
|
|
105
|
-
});
|
|
106
|
-
|
|
107
101
|
const relationBefore = event.entity?.[AUDIT_BEFORE_SNAPSHOT] ?? null;
|
|
108
102
|
|
|
109
|
-
|
|
103
|
+
// Same fix as CRUDService.prepareManyToManyAuditSnapshot: load each audit-tracked
|
|
104
|
+
// relation independently instead of joining them all into one query, so the row
|
|
105
|
+
// count of one relation doesn't multiply against every other joined relation.
|
|
106
|
+
const targetRepo = event.queryRunner.manager.getRepository(event.metadata.target as any);
|
|
107
|
+
const relationAfter = await targetRepo.findOne({
|
|
110
108
|
where: { id: entityId } as any,
|
|
111
|
-
relations: relations as any,
|
|
112
109
|
});
|
|
113
110
|
|
|
111
|
+
if (relationAfter) {
|
|
112
|
+
for (const field of auditRelationFields) {
|
|
113
|
+
(relationAfter as any)[field.name] = await event.queryRunner.manager
|
|
114
|
+
.createQueryBuilder()
|
|
115
|
+
.relation(event.metadata.target as any, field.name)
|
|
116
|
+
.of(entityId)
|
|
117
|
+
.loadMany();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
if (relationBefore && relationAfter) {
|
|
115
122
|
auditRelationFields.forEach(field => {
|
|
116
123
|
const oldIds = Array.isArray(relationBefore[field.name])
|