@solidxai/core 0.1.11-beta.1 → 0.1.11-beta.12
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/dist/commands/migrate-removed-fields.command.d.ts +19 -0
- package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
- package/dist/commands/migrate-removed-fields.command.js +77 -0
- package/dist/commands/migrate-removed-fields.command.js.map +1 -0
- package/dist/controllers/datasource-introspection.controller.d.ts +46 -1
- package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
- package/dist/controllers/datasource-introspection.controller.js +38 -0
- package/dist/controllers/datasource-introspection.controller.js.map +1 -1
- package/dist/controllers/service.controller.d.ts +1 -0
- package/dist/controllers/service.controller.d.ts.map +1 -1
- package/dist/controllers/service.controller.js +2 -2
- package/dist/controllers/service.controller.js.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
- package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
- package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
- package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
- package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
- package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
- package/dist/helpers/command.service.d.ts +2 -0
- package/dist/helpers/command.service.d.ts.map +1 -1
- package/dist/helpers/command.service.js +6 -2
- package/dist/helpers/command.service.js.map +1 -1
- package/dist/helpers/environment.helper.d.ts +1 -0
- package/dist/helpers/environment.helper.d.ts.map +1 -1
- package/dist/helpers/environment.helper.js +4 -0
- package/dist/helpers/environment.helper.js.map +1 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.js +4 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.d.ts +2 -1
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +9 -5
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.js +1 -1
- package/dist/helpers/module-metadata-helper.service.js.map +1 -1
- package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
- package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
- package/dist/helpers/nodemon-heartbeat.js +67 -0
- package/dist/helpers/nodemon-heartbeat.js.map +1 -0
- package/dist/helpers/solid-registry.d.ts +2 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/helpers/typeorm-db-helper.d.ts +1 -0
- package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
- package/dist/helpers/typeorm-db-helper.js +12 -0
- package/dist/helpers/typeorm-db-helper.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/repository/scheduled-job.repository.d.ts +2 -2
- package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
- package/dist/repository/scheduled-job.repository.js +6 -3
- package/dist/repository/scheduled-job.repository.js.map +1 -1
- package/dist/repository/security-rule.repository.d.ts +2 -2
- package/dist/repository/security-rule.repository.d.ts.map +1 -1
- package/dist/repository/security-rule.repository.js +6 -3
- package/dist/repository/security-rule.repository.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +11 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +163 -3
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +128 -34
- package/dist/services/api-key.service.d.ts.map +1 -1
- package/dist/services/api-key.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts +2 -0
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +32 -11
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +7 -5
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +25 -12
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +1 -0
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +27 -0
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts +63 -2
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +665 -188
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +3 -0
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/locale.service.d.ts +14 -0
- package/dist/services/locale.service.d.ts.map +1 -1
- package/dist/services/locale.service.js +83 -0
- package/dist/services/locale.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +1 -0
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +3 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +78 -0
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +28 -0
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/removed-field-migration.service.d.ts +30 -0
- package/dist/services/removed-field-migration.service.d.ts.map +1 -0
- package/dist/services/removed-field-migration.service.js +319 -0
- package/dist/services/removed-field-migration.service.js.map +1 -0
- package/dist/services/role-metadata.service.d.ts +1 -0
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +23 -2
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +244 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +120 -0
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/solid-ts-morph.service.d.ts +6 -0
- package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
- package/dist/services/solid-ts-morph.service.js +43 -0
- package/dist/services/solid-ts-morph.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +4 -7
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +5 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +76 -12
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +9 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +45 -11
- package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.d.ts.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.js +3 -2
- package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
- package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
- package/dist/subscribers/security-rule.subscriber.js +5 -3
- package/dist/subscribers/security-rule.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/migrate-removed-fields.command.ts +81 -0
- package/src/controllers/datasource-introspection.controller.ts +27 -1
- package/src/controllers/service.controller.ts +3 -2
- package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
- package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
- package/src/helpers/command.service.ts +10 -2
- package/src/helpers/environment.helper.ts +11 -0
- package/src/helpers/field-crud-managers/IntFieldCrudManager.ts +4 -1
- package/src/helpers/model-metadata-helper.service.ts +8 -3
- package/src/helpers/module-metadata-helper.service.ts +1 -1
- package/src/helpers/nodemon-heartbeat.ts +59 -0
- package/src/helpers/solid-registry.ts +7 -1
- package/src/helpers/typeorm-db-helper.ts +15 -0
- package/src/index.ts +3 -2
- package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
- package/src/interfaces.ts +1 -0
- package/src/repository/scheduled-job.repository.ts +10 -4
- package/src/repository/security-rule.repository.ts +10 -4
- package/src/seeders/module-metadata-seeder.service.ts +192 -2
- package/src/seeders/seed-data/solid-core-metadata.json +128 -34
- package/src/services/api-key.service.ts +36 -36
- package/src/services/authentication.service.ts +46 -12
- package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +11 -5
- package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
- package/src/services/crud-helper.service.ts +25 -12
- package/src/services/crud.service.ts +36 -11
- package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
- package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
- package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
- package/src/services/datasource-introspection.service.ts +827 -164
- package/src/services/import-transaction.service.ts +5 -0
- package/src/services/locale.service.ts +111 -3
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
- package/src/services/menu-item-metadata.service.ts +2 -1
- package/src/services/model-metadata.service.ts +109 -1
- package/src/services/module-metadata.service.ts +36 -0
- package/src/services/removed-field-migration.service.ts +334 -0
- package/src/services/role-metadata.service.ts +26 -2
- package/src/services/settings/default-settings-provider.service.ts +120 -0
- package/src/services/solid-ts-morph.service.ts +61 -1
- package/src/services/view-metadata.service.ts +4 -8
- package/src/solid-core.module.ts +4 -0
- package/src/subscribers/audit.subscriber.ts +101 -18
- package/src/subscribers/computed-entity-field.subscriber.ts +81 -28
- package/src/subscribers/created-by-updated-by.subscriber.ts +3 -0
- package/src/subscribers/field-metadata.subscriber.ts +3 -0
- package/src/subscribers/scheduled-job.subscriber.ts +3 -2
- package/src/subscribers/security-rule.subscriber.ts +8 -3
- package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
- package/dist/entities/legacy-common.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common.entity.js.map +0 -1
- /package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Injectable, Logger } from "@nestjs/common";
|
|
3
3
|
import { join, dirname, normalize, isAbsolute, basename } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { Project, Node, ObjectLiteralExpression, ArrayLiteralExpression, QuoteKind, IndentationText } from "ts-morph";
|
|
5
|
+
import { Project, Node, ObjectLiteralExpression, ArrayLiteralExpression, QuoteKind, IndentationText, SyntaxKind } from "ts-morph";
|
|
6
6
|
import { MethodDeclarationStructure, StructureKind } from "ts-morph";
|
|
7
7
|
|
|
8
8
|
type Bucket = "imports" | "providers" | "exports";
|
|
@@ -468,6 +468,66 @@ export class SolidTsMorphService {
|
|
|
468
468
|
return { staged: true, overwritten: false, skipped: false };
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
cleanupTypeormDatasourceEntity(
|
|
472
|
+
filePath: string,
|
|
473
|
+
entityImportPath: string,
|
|
474
|
+
entityClassName: string,
|
|
475
|
+
): { staged: boolean; skipped: boolean; removedImport: boolean; removedEntity: boolean } {
|
|
476
|
+
const abs = this.resolveRepoPath(filePath);
|
|
477
|
+
if (!existsSync(abs)) {
|
|
478
|
+
this.logger.warn(`cleanupTypeormDatasourceEntity: file not found at ${filePath}, skipping.`);
|
|
479
|
+
return { staged: false, skipped: true, removedImport: false, removedEntity: false };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const existing = this.project.getSourceFile(abs);
|
|
483
|
+
const sourceFile = existing
|
|
484
|
+
? existing
|
|
485
|
+
: this.project.createSourceFile(abs, readFileSync(abs, "utf8"), { overwrite: true });
|
|
486
|
+
|
|
487
|
+
let removedImport = false;
|
|
488
|
+
let removedEntity = false;
|
|
489
|
+
|
|
490
|
+
const importDeclaration = sourceFile.getImportDeclarations().find((declaration) =>
|
|
491
|
+
declaration.getModuleSpecifierValue().replace(/\\/g, "/") === entityImportPath.replace(/\\/g, "/"),
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
if (importDeclaration) {
|
|
495
|
+
const namedImport = importDeclaration.getNamedImports().find((named) => named.getName() === entityClassName);
|
|
496
|
+
if (namedImport) {
|
|
497
|
+
namedImport.remove();
|
|
498
|
+
removedImport = true;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (
|
|
502
|
+
importDeclaration.getNamedImports().length === 0 &&
|
|
503
|
+
!importDeclaration.getDefaultImport() &&
|
|
504
|
+
!importDeclaration.getNamespaceImport()
|
|
505
|
+
) {
|
|
506
|
+
importDeclaration.remove();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const entitiesDeclaration = sourceFile.getVariableDeclaration("entities");
|
|
511
|
+
const entitiesArray = entitiesDeclaration?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);
|
|
512
|
+
if (entitiesArray) {
|
|
513
|
+
const elements = entitiesArray.getElements();
|
|
514
|
+
for (let i = elements.length - 1; i >= 0; i--) {
|
|
515
|
+
if (elements[i].getText().replace(/\s+/g, "") === entityClassName) {
|
|
516
|
+
entitiesArray.removeElement(i);
|
|
517
|
+
removedEntity = true;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (!removedImport && !removedEntity) {
|
|
523
|
+
return { staged: false, skipped: true, removedImport, removedEntity };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
this.dirtySourceFiles.add(abs);
|
|
527
|
+
this.logger.log(`Staged datasource cleanup in ${this.rel(abs)} for entity ${entityClassName}`);
|
|
528
|
+
return { staged: true, skipped: false, removedImport, removedEntity };
|
|
529
|
+
}
|
|
530
|
+
|
|
471
531
|
|
|
472
532
|
|
|
473
533
|
}
|
|
@@ -151,15 +151,11 @@ export class ViewMetadataService extends CRUDService<ViewMetadata> {
|
|
|
151
151
|
return { records: [], defaultEntityLocaleId: null };
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (entityRecord.localeName === defaultLocale?.locale) {
|
|
158
|
-
defaultEntityLocaleId = entityRecord.id;
|
|
159
|
-
this.logger.debug(`Editing default locale record with id ${defaultEntityLocaleId}`);
|
|
154
|
+
const defaultEntityLocaleId = entityRecord.defaultEntityLocaleId || entityRecord.id;
|
|
155
|
+
if (entityRecord.defaultEntityLocaleId) {
|
|
156
|
+
this.logger.debug(`Editing translated locale record. Translation root id: ${defaultEntityLocaleId}`);
|
|
160
157
|
} else {
|
|
161
|
-
|
|
162
|
-
this.logger.debug(`Editing non-default locale record. DefaultEntityLocaleId: ${defaultEntityLocaleId}`);
|
|
158
|
+
this.logger.debug(`Editing translation root record with id ${defaultEntityLocaleId}`);
|
|
163
159
|
}
|
|
164
160
|
|
|
165
161
|
const records = await currentEntityRepository.find({
|
package/src/solid-core.module.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { MulterModule } from "@nestjs/platform-express";
|
|
14
14
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
15
15
|
import { RemoveFieldsCommand } from "./commands/remove-fields.command";
|
|
16
|
+
import { MigrateRemovedFieldsCommand } from "./commands/migrate-removed-fields.command";
|
|
16
17
|
import { FieldMetadataController } from "./controllers/field-metadata.controller";
|
|
17
18
|
import { DashboardController } from "./controllers/dashboard.controller";
|
|
18
19
|
import { MediaStorageProviderMetadataController } from "./controllers/media-storage-provider-metadata.controller";
|
|
@@ -47,6 +48,7 @@ import { ListOfValuesService } from "./services/list-of-values.service";
|
|
|
47
48
|
import { MediaStorageProviderMetadataService } from "./services/media-storage-provider-metadata.service";
|
|
48
49
|
import { MediaService } from "./services/media.service";
|
|
49
50
|
import { ModelMetadataService } from "./services/model-metadata.service";
|
|
51
|
+
import { RemovedFieldMigrationService } from "./services/removed-field-migration.service";
|
|
50
52
|
import { ModuleMetadataExplorerService } from "./services/module-metadata-explorer.service";
|
|
51
53
|
import { ModuleMetadataService } from "./services/module-metadata.service";
|
|
52
54
|
import { ModulePackageService } from "./services/module-package.service";
|
|
@@ -545,6 +547,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
545
547
|
FieldMetadataService,
|
|
546
548
|
DashboardRuntimeService,
|
|
547
549
|
RemoveFieldsCommand,
|
|
550
|
+
MigrateRemovedFieldsCommand,
|
|
548
551
|
RefreshModelCommand,
|
|
549
552
|
RefreshModuleCommand,
|
|
550
553
|
InfoCommand,
|
|
@@ -791,6 +794,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
791
794
|
UserViewMetadataRepository,
|
|
792
795
|
ModelMetadataRepository,
|
|
793
796
|
ModuleMetadataRepository,
|
|
797
|
+
RemovedFieldMigrationService,
|
|
794
798
|
ActionMetadataRepository,
|
|
795
799
|
MediaStorageProviderMetadataRepository,
|
|
796
800
|
FixturesService,
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { Injectable, Logger, Scope } from '@nestjs/common';
|
|
2
|
+
import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
|
|
2
3
|
import { lowerFirst } from 'src/helpers/string.helper';
|
|
3
4
|
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
4
5
|
import { DataSource, EntityMetadata, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
|
|
5
6
|
import { AuditQueuePayload } from 'src/interfaces';
|
|
7
|
+
import { isEmbeddedDb } from 'src/helpers/environment.helper';
|
|
6
8
|
import { RequestContextService } from 'src/services/request-context.service';
|
|
7
9
|
import { PublisherFactory } from 'src/services/queues/publisher-factory.service';
|
|
10
|
+
const AUDIT_BEFORE_SNAPSHOT = '__auditBeforeSnapshot';
|
|
8
11
|
|
|
9
|
-
@Injectable({scope: Scope.TRANSIENT})
|
|
12
|
+
@Injectable({ scope: Scope.TRANSIENT })
|
|
10
13
|
export class AuditSubscriber implements EntitySubscriberInterface {
|
|
11
14
|
private readonly logger = new Logger(AuditSubscriber.name);
|
|
12
15
|
private dataSource: DataSource;
|
|
@@ -14,6 +17,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
14
17
|
private readonly publisherFactory: PublisherFactory<AuditQueuePayload>,
|
|
15
18
|
private readonly solidRegistry: SolidRegistry,
|
|
16
19
|
private readonly requestContextService: RequestContextService,
|
|
20
|
+
private readonly modelMetadataHelperService: ModelMetadataHelperService,
|
|
17
21
|
) { }
|
|
18
22
|
|
|
19
23
|
bindToDataSource(dataSource: DataSource) {
|
|
@@ -53,20 +57,85 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
53
57
|
|
|
54
58
|
async afterUpdate(event: UpdateEvent<any>) {
|
|
55
59
|
if (!this.shouldTrackAudit(event.metadata)) return;
|
|
60
|
+
const entityId = event.entity?.id ?? event.databaseEntity?.id ?? (event as any).entityId ?? null;
|
|
61
|
+
let before = event.databaseEntity ?? null;
|
|
62
|
+
let after = event.entity ?? null;
|
|
63
|
+
const updatedColumnNames = (event.updatedColumns ?? []).map(c => c.propertyName);
|
|
64
|
+
|
|
65
|
+
const auditResult = await this.prepareManyToManyAuditUpdateSnapshot(event, entityId, updatedColumnNames);
|
|
66
|
+
|
|
67
|
+
if (auditResult) {
|
|
68
|
+
before = auditResult.before;
|
|
69
|
+
after = auditResult.after;
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
this.enqueue(event, {
|
|
57
73
|
eventType: 'update',
|
|
58
74
|
modelName: event.metadata.name,
|
|
59
|
-
entityId:
|
|
75
|
+
entityId: entityId,
|
|
60
76
|
occurredAt: new Date().toISOString(),
|
|
61
|
-
after:
|
|
77
|
+
after: after,
|
|
62
78
|
// databaseEntity is only populated when the entity was fetched first (save() path).
|
|
63
79
|
// QueryBuilder update() leaves this undefined; postAuditMessageOnUpdate guards for it.
|
|
64
|
-
before:
|
|
65
|
-
updatedColumnNames:
|
|
80
|
+
before: before,
|
|
81
|
+
updatedColumnNames: updatedColumnNames,
|
|
66
82
|
userId: this.activeUserId(),
|
|
67
83
|
});
|
|
68
84
|
}
|
|
69
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Resolves before/after snapshots for audit-enabled many-to-many relations
|
|
88
|
+
* and updates changed relation field names for audit tracking.
|
|
89
|
+
*/
|
|
90
|
+
private async prepareManyToManyAuditUpdateSnapshot(event: UpdateEvent<any>, entityId: number | null, updatedColumnNames: string[]): Promise<{ before: any; after: any } | null> {
|
|
91
|
+
|
|
92
|
+
// On embedded PGlite, pass the transaction's manager so the query runs
|
|
93
|
+
// on the active connection. On regular Postgres, use the default DataSource.
|
|
94
|
+
const mgr = isEmbeddedDb() ? event.manager : this.dataSource;
|
|
95
|
+
const auditRelationFields = (await this.modelMetadataHelperService.loadFieldHierarchy(lowerFirst(event.metadata.name), isEmbeddedDb() ? event.manager : undefined)).filter(field =>
|
|
96
|
+
field.enableAuditTracking &&
|
|
97
|
+
field.type === 'relation' &&
|
|
98
|
+
field.relationType !== 'one-to-many'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (!entityId || auditRelationFields.length === 0) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const relations: Record<string, boolean> = {};
|
|
106
|
+
|
|
107
|
+
auditRelationFields.forEach(field => {
|
|
108
|
+
relations[field.name] = true;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const relationBefore = event.entity?.[AUDIT_BEFORE_SNAPSHOT] ?? null;
|
|
112
|
+
|
|
113
|
+
const relationAfter = await mgr.getRepository(event.metadata.target as any).findOne({
|
|
114
|
+
where: { id: entityId } as any,
|
|
115
|
+
relations: relations as any,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (relationBefore && relationAfter) {
|
|
119
|
+
auditRelationFields.forEach(field => {
|
|
120
|
+
const oldIds = Array.isArray(relationBefore[field.name])
|
|
121
|
+
? relationBefore[field.name].map(item => item.id).sort()
|
|
122
|
+
: [];
|
|
123
|
+
|
|
124
|
+
const newIds = Array.isArray(relationAfter[field.name])
|
|
125
|
+
? relationAfter[field.name].map(item => item.id).sort()
|
|
126
|
+
: [];
|
|
127
|
+
|
|
128
|
+
if ((oldIds.length !== newIds.length || JSON.stringify(oldIds) !== JSON.stringify(newIds))
|
|
129
|
+
&& !updatedColumnNames.includes(field.name)) {
|
|
130
|
+
updatedColumnNames.push(field.name);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
before: relationBefore ?? event.databaseEntity ?? null,
|
|
136
|
+
after: relationAfter ?? event.entity ?? null,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
70
139
|
async afterRemove(event: RemoveEvent<any>) {
|
|
71
140
|
if (!this.shouldTrackAudit(event.metadata)) return;
|
|
72
141
|
this.enqueue(event, {
|
|
@@ -84,24 +153,38 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
84
153
|
const batch = this.perTxn.get(event.queryRunner) ?? [];
|
|
85
154
|
this.perTxn.delete(event.queryRunner);
|
|
86
155
|
|
|
87
|
-
|
|
88
|
-
// allSettled: publish in parallel; a single failure does not block the rest.
|
|
89
|
-
const results = await Promise.allSettled(
|
|
90
|
-
batch.map(payload => this.publisherFactory.publish({ payload }, 'ChatterQueuePublisher'))
|
|
91
|
-
);
|
|
156
|
+
if (batch.length === 0) return;
|
|
92
157
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
});
|
|
158
|
+
if (isEmbeddedDb()) {
|
|
159
|
+
setImmediate(() => void this.publishAuditBatch(batch));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await this.publishAuditBatch(batch);
|
|
101
164
|
}
|
|
102
165
|
|
|
103
166
|
afterTransactionRollback(event: { queryRunner: any }) {
|
|
104
167
|
// Drop buffered payloads; the write never happened.
|
|
105
168
|
this.perTxn.delete(event.queryRunner);
|
|
106
169
|
}
|
|
170
|
+
|
|
171
|
+
private async publishAuditBatch(batch: AuditQueuePayload[]) {
|
|
172
|
+
try {
|
|
173
|
+
// allSettled: publish in parallel; a single failure does not block the rest.
|
|
174
|
+
const results = await Promise.allSettled(
|
|
175
|
+
batch.map(payload => this.publisherFactory.publish({ payload }, 'ChatterQueuePublisher'))
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
results.forEach((result, i) => {
|
|
179
|
+
if (result.status === 'rejected') {
|
|
180
|
+
this.logger.error(
|
|
181
|
+
`Failed to publish audit event for ${batch[i].modelName}#${batch[i].entityId}`,
|
|
182
|
+
result.reason,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
this.logger.error('Failed to publish audit batch', error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
107
190
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { camelCase } from 'lodash';
|
|
2
|
-
import { Delete } from "@aws-sdk/client-s3";
|
|
3
2
|
import { forwardRef, Inject, Injectable, InternalServerErrorException, Logger, Scope } from "@nestjs/common";
|
|
4
|
-
import { model } from "mongoose";
|
|
5
3
|
import { ComputedFieldTriggerOperation } from "src/dtos/create-field-metadata.dto";
|
|
4
|
+
import { isEmbeddedDb } from "src/helpers/environment.helper";
|
|
6
5
|
import { ComputedFieldMetadata, SolidRegistry, TypeOrmEventContext } from "src/helpers/solid-registry";
|
|
7
6
|
import { IEntityPreComputeFieldProvider } from "src/interfaces";
|
|
8
7
|
import { PublisherFactory } from "src/services/queues/publisher-factory.service";
|
|
@@ -14,19 +13,22 @@ export interface ComputedFieldEvaluationPayload extends ComputedFieldMetadata {
|
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
@Injectable({ scope: Scope.TRANSIENT })
|
|
17
|
-
// @EventSubscriber()
|
|
18
16
|
export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface {
|
|
19
17
|
private readonly logger = new Logger(this.constructor.name);
|
|
20
18
|
private dataSource: DataSource;
|
|
19
|
+
|
|
20
|
+
// Per-transaction buffer for post-event evaluation jobs. Entries are
|
|
21
|
+
// flushed in afterTransactionCommit so the publish (which uses the
|
|
22
|
+
// default DataSource) never races the in-flight transaction's connection.
|
|
23
|
+
// On PGlite (pool size 1), the flush is deferred via setImmediate to
|
|
24
|
+
// ensure the transaction's connection has been released first.
|
|
25
|
+
private perTxn = new WeakMap<any, { computedField: ComputedFieldMetadata<any>; databaseEntity: any }[]>();
|
|
26
|
+
|
|
21
27
|
constructor(
|
|
22
|
-
// @InjectDataSource()
|
|
23
|
-
// private readonly dataSource: DataSource,
|
|
24
28
|
private readonly solidRegistry: SolidRegistry,
|
|
25
29
|
@Inject(forwardRef(() => PublisherFactory))
|
|
26
30
|
private readonly publisherFactory: PublisherFactory<ComputedFieldEvaluationPayload>
|
|
27
|
-
// private readonly computedFieldPublisher: ComputedFieldEvaluationPublisherDatabase,
|
|
28
31
|
) {
|
|
29
|
-
// this.dataSource.subscribers.push(this);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
bindToDataSource(dataSource: DataSource) {
|
|
@@ -43,47 +45,97 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
43
45
|
async beforeUpdate(event: UpdateEvent<any>): Promise<any> {
|
|
44
46
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
|
|
45
47
|
const eventContext = this.sanitizeEventContext(event, 'beforeUpdate');
|
|
46
|
-
// await this.handleComputedFieldEvaluation(event.databaseEntity, ComputedFieldTriggerOperation.beforeUpdate, modelName, eventContext);
|
|
47
48
|
await this.handleComputedFieldEvaluation(event.entity, ComputedFieldTriggerOperation.beforeUpdate, modelName, eventContext);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
afterInsert(event: InsertEvent<any>) {
|
|
51
52
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
|
|
52
53
|
const eventContext = this.sanitizeEventContext(event, 'afterInsert');
|
|
53
|
-
this.
|
|
54
|
+
this.handlePostEventJobs(event, event.entity, ComputedFieldTriggerOperation.afterInsert, modelName, eventContext);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
afterUpdate(event: UpdateEvent<any>) {
|
|
57
58
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? event.databaseEntity?.constructor?.name ?? '');
|
|
58
59
|
const eventContext = this.sanitizeEventContext(event, 'afterUpdate');
|
|
59
|
-
|
|
60
|
-
this.handleComputedFieldEvaluationJob(event.entity, ComputedFieldTriggerOperation.afterUpdate, modelName, eventContext);
|
|
60
|
+
this.handlePostEventJobs(event, event.entity, ComputedFieldTriggerOperation.afterUpdate, modelName, eventContext);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
afterRemove(event: RemoveEvent<any>) {
|
|
64
64
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? event.databaseEntity?.constructor?.name ?? '');
|
|
65
65
|
const eventContext = this.sanitizeEventContext(event, 'afterRemove');
|
|
66
|
-
this.
|
|
66
|
+
this.handlePostEventJobs(event, event.databaseEntity, ComputedFieldTriggerOperation.afterRemove, modelName, eventContext);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
//FIXME: Need to add support for beforeRemove, beforeSoftRemove, afterSoftRemove, beforeRecover, afterRecover
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
// --------- post-event dispatch (pglite vs non-pglite) ----------
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* On embedded PGlite (pool size 1): buffer payloads on the queryRunner and
|
|
75
|
+
* flush in afterTransactionCommit (deferred via setImmediate) so the publish
|
|
76
|
+
* never tries to get a second connection while the transaction is active.
|
|
77
|
+
*
|
|
78
|
+
* On regular Postgres: fire-and-forget publish immediately (original behaviour),
|
|
79
|
+
* since the pool has spare connections.
|
|
80
|
+
*/
|
|
81
|
+
private handlePostEventJobs(
|
|
82
|
+
event: InsertEvent<any> | UpdateEvent<any> | RemoveEvent<any>,
|
|
83
|
+
entity: any,
|
|
84
|
+
currentOperation: ComputedFieldTriggerOperation,
|
|
85
|
+
modelName: string,
|
|
86
|
+
eventContext?: TypeOrmEventContext,
|
|
87
|
+
) {
|
|
88
|
+
if (!entity) return;
|
|
89
|
+
|
|
75
90
|
const computedFieldsTobeEvaluated = this.getComputedFieldsForEvaluation(
|
|
76
91
|
this.solidRegistry.getComputedFieldMetadata(),
|
|
77
92
|
currentOperation,
|
|
78
93
|
modelName
|
|
79
94
|
);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
if (computedFieldsTobeEvaluated.length === 0) return;
|
|
96
|
+
|
|
97
|
+
if (isEmbeddedDb()) {
|
|
98
|
+
const qr = event.queryRunner;
|
|
99
|
+
const arr = this.perTxn.get(qr) ?? [];
|
|
100
|
+
for (const computedField of computedFieldsTobeEvaluated) {
|
|
101
|
+
arr.push({
|
|
102
|
+
computedField: this.attachContext(computedField, eventContext),
|
|
103
|
+
databaseEntity: entity,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
this.perTxn.set(qr, arr);
|
|
107
|
+
} else {
|
|
108
|
+
for (const computedField of computedFieldsTobeEvaluated) {
|
|
109
|
+
this.enqueueComputedFieldEvaluationJob(
|
|
110
|
+
this.attachContext(computedField, eventContext),
|
|
111
|
+
entity,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
83
114
|
}
|
|
84
115
|
}
|
|
85
116
|
|
|
86
|
-
|
|
117
|
+
// --------- transaction lifecycle (pglite only) ----------
|
|
118
|
+
async afterTransactionCommit(event: { queryRunner: any }) {
|
|
119
|
+
const batch = this.perTxn.get(event.queryRunner) ?? [];
|
|
120
|
+
this.perTxn.delete(event.queryRunner);
|
|
121
|
+
if (batch.length === 0) return;
|
|
122
|
+
|
|
123
|
+
// Deferred via setImmediate so the transaction's connection is released
|
|
124
|
+
// back to the (size-1) pool before the publish tries to acquire it.
|
|
125
|
+
setImmediate(() => void this.flushBatch(batch));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
afterTransactionRollback(event: { queryRunner: any }) {
|
|
129
|
+
this.perTxn.delete(event.queryRunner);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private async flushBatch(batch: { computedField: ComputedFieldMetadata<any>; databaseEntity: any }[]) {
|
|
133
|
+
for (const { computedField, databaseEntity } of batch) {
|
|
134
|
+
this.enqueueComputedFieldEvaluationJob(computedField, databaseEntity);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async handleComputedFieldEvaluation(entity: any, currentOperation: ComputedFieldTriggerOperation, modelName: string, eventContext?: TypeOrmEventContext): Promise<void> {
|
|
87
139
|
if (!entity) {
|
|
88
140
|
return;
|
|
89
141
|
}
|
|
@@ -92,9 +144,8 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
92
144
|
currentOperation,
|
|
93
145
|
modelName
|
|
94
146
|
);
|
|
95
|
-
//TODO: We can add a feature i.e dependsOn, where we can check if the computed field depends on other computed fields and evaluate them first
|
|
96
147
|
for (const computedField of computedFieldsTobeEvaluated) {
|
|
97
|
-
this.
|
|
148
|
+
await this.evaluateComputedField(this.attachContext(computedField, eventContext), entity, currentOperation);
|
|
98
149
|
}
|
|
99
150
|
}
|
|
100
151
|
|
|
@@ -147,16 +198,14 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
147
198
|
}
|
|
148
199
|
}
|
|
149
200
|
|
|
150
|
-
private enqueueComputedFieldEvaluationJob(computedField: ComputedFieldMetadata<any>, databaseEntity: any
|
|
201
|
+
private enqueueComputedFieldEvaluationJob(computedField: ComputedFieldMetadata<any>, databaseEntity: any) {
|
|
202
|
+
const { manager: _manager, ...serializableEventContext } = computedField.eventContext ?? {};
|
|
151
203
|
const payload = {
|
|
152
204
|
...computedField,
|
|
205
|
+
eventContext: serializableEventContext,
|
|
153
206
|
databaseEntity,
|
|
154
|
-
// eventContext,
|
|
155
207
|
};
|
|
156
|
-
this.publisherFactory.publish({ payload }, 'ComputedFieldEvaluationPublisher')
|
|
157
|
-
// this.computedFieldPublisher.publish({
|
|
158
|
-
// payload
|
|
159
|
-
// });
|
|
208
|
+
this.publisherFactory.publish({ payload }, 'ComputedFieldEvaluationPublisher');
|
|
160
209
|
}
|
|
161
210
|
|
|
162
211
|
private attachContext<T extends ComputedFieldMetadata<any>>(computedField: T, eventContext?: any): T {
|
|
@@ -175,6 +224,10 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
175
224
|
const base: TypeOrmEventContext = {
|
|
176
225
|
metadataName: event.metadata?.name,
|
|
177
226
|
eventType: eventType,
|
|
227
|
+
// Only pass the transaction's EntityManager for embedded DB (PGlite),
|
|
228
|
+
// where a second pooled connection mid-transaction would deadlock.
|
|
229
|
+
// On regular Postgres, providers use their injected EntityManager.
|
|
230
|
+
...(isEmbeddedDb() ? { manager: event.manager } : {}),
|
|
178
231
|
};
|
|
179
232
|
if ("entityId" in event && event.entityId) {
|
|
180
233
|
base.entityId = event.entityId;
|
|
@@ -38,6 +38,9 @@ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
|
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// NOTE: If re-enabling loadUser below, query via `event.queryRunner.manager`, NOT
|
|
42
|
+
// `this.defaultDataSource.getRepository(...)`. A second connection opened mid-transaction
|
|
43
|
+
// deadlocks single-threaded engines (PGlite).
|
|
41
44
|
// const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
|
|
42
45
|
// if (isInsert) {
|
|
43
46
|
// event.entity.createdBy = loadedUser?.id;
|
|
@@ -21,6 +21,9 @@ export class FieldMetadataSubscriber implements EntitySubscriberInterface<FieldM
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
async afterUpdate(event: UpdateEvent<FieldMetadata>): Promise<void> {
|
|
24
|
+
// NOTE: If re-enabling the block below, use `event.queryRunner.manager` (the active
|
|
25
|
+
// transaction's connection), NOT `this.dataSource.getRepository(...)`. A second
|
|
26
|
+
// connection opened here mid-transaction deadlocks single-threaded engines (PGlite).
|
|
24
27
|
// If a field of type relation.many-to-one is marked for removal, and this field had an inverse relation created
|
|
25
28
|
// It means there is a field of type relation.one-to-many in the co-model, which needs to be removed
|
|
26
29
|
// if (event.entity && event.entity.isMarkedForRemoval && event.entity.type === 'relation' && event.entity.relationType === 'many-to-one' && event.entity.relationCreateInverse === true) {
|
|
@@ -4,6 +4,7 @@ import * as fs from "fs/promises";
|
|
|
4
4
|
import { CreateScheduledJobDto } from "src/dtos/create-scheduled-job.dto";
|
|
5
5
|
import { ModuleMetadata } from "src/entities/module-metadata.entity";
|
|
6
6
|
import { ScheduledJob } from "src/entities/scheduled-job.entity";
|
|
7
|
+
import { isEmbeddedDb } from "src/helpers/environment.helper";
|
|
7
8
|
import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
|
|
8
9
|
import { ScheduledJobRepository } from "src/repository/scheduled-job.repository";
|
|
9
10
|
import {
|
|
@@ -83,7 +84,7 @@ export class ScheduledJobSubscriber
|
|
|
83
84
|
return;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
const moduleMetadataRepo = event.queryRunner.manager.getRepository(ModuleMetadata);
|
|
87
|
+
const moduleMetadataRepo = (isEmbeddedDb() ? event.queryRunner.manager : this.dataSource).getRepository(ModuleMetadata);
|
|
87
88
|
const populatedModuleMetadata = await moduleMetadataRepo.findOne({
|
|
88
89
|
where: { id: moduleMetadata.id },
|
|
89
90
|
});
|
|
@@ -160,7 +161,7 @@ export class ScheduledJobSubscriber
|
|
|
160
161
|
(job) => job.scheduleName === jobName
|
|
161
162
|
);
|
|
162
163
|
// Insert or update job in metadata
|
|
163
|
-
const jobDto: CreateScheduledJobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob as ScheduledJob);
|
|
164
|
+
const jobDto: CreateScheduledJobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob as ScheduledJob, isEmbeddedDb() ? entityManager : undefined);
|
|
164
165
|
// Strip out bookkeeping/runtime fields before persisting to metadata JSON.
|
|
165
166
|
const {
|
|
166
167
|
moduleId,
|
|
@@ -4,6 +4,7 @@ import * as fs from 'fs/promises'; // Use the Promise-based version of fs for as
|
|
|
4
4
|
import { SOLID_CORE_MODULE_NAME } from 'src/constants';
|
|
5
5
|
import { ModelMetadata } from 'src/entities/model-metadata.entity';
|
|
6
6
|
import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
7
|
+
import { isEmbeddedDb } from "src/helpers/environment.helper";
|
|
7
8
|
import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
|
|
8
9
|
import { SecurityRuleRepository } from 'src/repository/security-rule.repository';
|
|
9
10
|
import { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from "typeorm";
|
|
@@ -40,7 +41,11 @@ export class SecurityRuleSubscriber implements EntitySubscriberInterface<Securit
|
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
// On embedded PGlite, use the transaction's connection to avoid a
|
|
45
|
+
// second pooled connection mid-transaction. On regular Postgres, use
|
|
46
|
+
// the default DataSource (original behaviour).
|
|
47
|
+
const mgr = isEmbeddedDb() ? event.manager : this.dataSource;
|
|
48
|
+
const modelMetadataRepo = mgr.getRepository(ModelMetadata);
|
|
44
49
|
const populatedModelMetadata = await modelMetadataRepo.findOne({
|
|
45
50
|
where: {
|
|
46
51
|
id: modelMetadata.id
|
|
@@ -67,12 +72,12 @@ export class SecurityRuleSubscriber implements EntitySubscriberInterface<Securit
|
|
|
67
72
|
|
|
68
73
|
if (metaData.securityRules) {
|
|
69
74
|
const securityRuleIndex = metaData.securityRules?.findIndex((ruleFromFile: { name: string }) => ruleFromFile.name === securityRule.name);
|
|
70
|
-
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule)
|
|
75
|
+
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule, isEmbeddedDb() ? event.manager : undefined)
|
|
71
76
|
metaData.securityRules[securityRuleIndex] = { ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) }
|
|
72
77
|
}
|
|
73
78
|
else {
|
|
74
79
|
const securityRules = []
|
|
75
|
-
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule)
|
|
80
|
+
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule, isEmbeddedDb() ? event.manager : undefined)
|
|
76
81
|
securityRules.push({ ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) })
|
|
77
82
|
metaData.securityRules = securityRules
|
|
78
83
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-common-with-id.entity.d.ts","sourceRoot":"","sources":["../../src/entities/legacy-common-with-id.entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAEtG,8BACsB,iCAAkC,SAAQ,gCAAgC;IAI5F,EAAE,EAAE,MAAM,CAAA;CACb"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-common-with-id.entity.js","sourceRoot":"","sources":["../../src/entities/legacy-common-with-id.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,yDAAoD;AACpD,qCAA4C;AAC5C,iEAAsG;AAG/F,IAAe,iCAAiC,GAAhD,MAAe,iCAAkC,SAAQ,uDAAgC;;;;CAK/F,CAAA;AALqB,8EAAiC;AAInD;IAHC,IAAA,0BAAM,GAAE;IACR,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,iDAA0B,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACnF,IAAA,mBAAS,EAAC,WAAW,CAAC;;6DACb;4CAJQ,iCAAiC;IADtD,IAAA,2BAAO,GAAE;GACY,iCAAiC,CAKtD","sourcesContent":["import { Exclude, Expose } from \"class-transformer\";\nimport { Column, Generated } from \"typeorm\";\nimport { LEGACY_TABLE_FIELDS_PREFIX, LegacyCommonEntityWithExistingId } from \"./legacy-common.entity\";\n\n@Exclude()\nexport abstract class LegacyCommonEntityWithGeneratedId extends LegacyCommonEntityWithExistingId {\n @Expose()\n @Column({ type: 'integer', name: `${LEGACY_TABLE_FIELDS_PREFIX}_id`, unique: true })\n @Generated(\"increment\")\n id: number\n}"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-common.entity.d.ts","sourceRoot":"","sources":["../../src/entities/legacy-common.entity.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,8BACsB,gCAAgC;IAOlD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IAGvB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IAIvB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IAGvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAI9B,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IAIzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAI1B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAgBrC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAIzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-common.entity.js","sourceRoot":"","sources":["../../src/entities/legacy-common.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,yDAAoD;AACpD,qGAAgG;AAChG,qCAA8F;AAEjF,QAAA,0BAA0B,GAAG,IAAI,CAAC;AAGxC,IAAe,gCAAgC,GAA/C,MAAe,gCAAgC;;;;CAkDrD,CAAA;AAlDqB,4EAAgC;AAOlD;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,aAAa,EAAE,WAAW,EAAE,sDAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACnH,IAAI;mEAAQ;AAGvB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,aAAa,EAAE,WAAW,EAAE,sDAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACnH,IAAI;mEAAQ;AAIvB;IAFC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,aAAa,EAAE,WAAW,EAAE,sDAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC7H,IAAA,eAAK,GAAE;8BACG,IAAI;mEAAQ;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,kBAAkB,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wEAC5E;AAI9B;IAFC,IAAA,0BAAM,GAAE;IACR,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sDAAwB,EAAE,CAAC;8BACxH,IAAI;qEAAQ;AAIzB;IAFC,IAAA,0BAAM,GAAE;IACR,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,kCAA0B,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oEACpF;AAI1B;IAFC,IAAA,0BAAM,GAAE;IACR,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,kCAA0B,2BAA2B,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+EAClF;AAgBrC;IAFC,IAAA,0BAAM,GAAE;IACR,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mEACvD;AAIzB;IAFC,IAAA,0BAAM,GAAE;IACR,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,GAAG,kCAA0B,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mEACvD;2CAjDP,gCAAgC;IADrD,IAAA,2BAAO,GAAE;GACY,gCAAgC,CAkDrD","sourcesContent":["import { Exclude, Expose } from \"class-transformer\";\nimport { LocalDateTimeTransformer } from \"src/transformers/typeorm/local-date-time-transformer\";\nimport { Column, CreateDateColumn, DeleteDateColumn, Index, UpdateDateColumn } from \"typeorm\";\n\nexport const LEGACY_TABLE_FIELDS_PREFIX = 'ss';\n\n@Exclude()\nexport abstract class LegacyCommonEntityWithExistingId {\n // @Expose()\n // @Column({ type: 'integer', name: `${LEGACY_TABLE_FIELDS_PREFIX}_id`, unique: true })\n // @Generated(\"increment\")\n // id: number\n\n @CreateDateColumn({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_created_at`, transformer: LocalDateTimeTransformer, nullable: true })\n createdAt: Date | null;\n\n @UpdateDateColumn({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_updated_at`, transformer: LocalDateTimeTransformer, nullable: true })\n updatedAt: Date | null;\n\n @DeleteDateColumn({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_deleted_at`, transformer: LocalDateTimeTransformer, nullable: true })\n @Index()\n deletedAt: Date | null;\n\n @Column({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_deleted_tracker`, default: \"not-deleted\", nullable: true })\n deletedTracker: string | null;\n\n @Expose()\n @Column({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_published_at`, default: null, nullable: true, transformer: LocalDateTimeTransformer })\n publishedAt: Date | null;\n\n @Expose()\n @Column({ type: \"varchar\", name: `${LEGACY_TABLE_FIELDS_PREFIX}_locale_name`, default: null, nullable: true })\n localeName: string | null;\n\n @Expose()\n @Column({ type: \"int\", name: `${LEGACY_TABLE_FIELDS_PREFIX}_default_entity_locale_id`, default: null, nullable: true })\n defaultEntityLocaleId: number | null;\n\n // @Expose()\n // @Type( () => require('./user.entity').User?.default ?? require('./user.entity').User )\n // @ManyToOne(() => require('./user.entity').User?.default ?? require('./user.entity').User, { nullable: true })\n // @JoinColumn({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_created_by_id` })\n // createdBy: User;\n\n // @Expose()\n // @Type( () => require('./user.entity').User?.default ?? require('./user.entity').User )\n // @ManyToOne(() => require('./user.entity').User?.default ?? require('./user.entity').User, { nullable: true })\n // @JoinColumn({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_updated_by_id` })\n // updatedBy: User; \n\n @Expose()\n @Column({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_created_by_id`, nullable: true })\n createdBy: number | null;\n\n @Expose()\n @Column({ name: `${LEGACY_TABLE_FIELDS_PREFIX}_updated_by_id`, nullable: true })\n updatedBy: number | null;\n}\n"]}
|
/package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts}
RENAMED
|
File without changes
|