@solidxai/core 0.1.11-beta.11 → 0.1.11-beta.13
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/controllers/media.controller.js +0 -2
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/helpers/environment.helper.d.ts +0 -1
- package/dist/helpers/environment.helper.d.ts.map +1 -1
- package/dist/helpers/environment.helper.js +0 -4
- package/dist/helpers/environment.helper.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.d.ts +1 -2
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +2 -6
- 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/solid-registry.d.ts +1 -2
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.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 +3 -6
- 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 +3 -6
- package/dist/repository/security-rule.repository.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 +5 -7
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +7 -0
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts +1 -0
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +14 -5
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +0 -16
- 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 +0 -28
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +2 -8
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +0 -8
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +3 -36
- 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 +2 -3
- 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 +3 -5
- package/dist/subscribers/security-rule.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/controllers/media.controller.ts +1 -1
- package/src/helpers/environment.helper.ts +0 -11
- package/src/helpers/model-metadata-helper.service.ts +2 -7
- package/src/helpers/module-metadata-helper.service.ts +1 -1
- package/src/helpers/solid-registry.ts +1 -7
- package/src/index.ts +0 -1
- package/src/repository/scheduled-job.repository.ts +4 -10
- package/src/repository/security-rule.repository.ts +4 -10
- package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +5 -11
- package/src/services/crud-helper.service.ts +7 -0
- package/src/services/datasource-introspection.service.ts +18 -5
- package/src/services/model-metadata.service.ts +0 -22
- package/src/services/module-metadata.service.ts +0 -36
- package/src/subscribers/audit.subscriber.ts +2 -11
- package/src/subscribers/computed-entity-field.subscriber.ts +6 -63
- package/src/subscribers/created-by-updated-by.subscriber.ts +0 -3
- package/src/subscribers/field-metadata.subscriber.ts +0 -3
- package/src/subscribers/scheduled-job.subscriber.ts +2 -3
- package/src/subscribers/security-rule.subscriber.ts +3 -8
- package/dist/helpers/nodemon-heartbeat.d.ts +0 -5
- package/dist/helpers/nodemon-heartbeat.d.ts.map +0 -1
- package/dist/helpers/nodemon-heartbeat.js +0 -67
- package/dist/helpers/nodemon-heartbeat.js.map +0 -1
- package/src/helpers/nodemon-heartbeat.ts +0 -59
|
@@ -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,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;
|
|
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}"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-metadata.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/field-metadata.subscriber.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAgC,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAGlE,qBACa,uBAAwB,YAAW,yBAAyB,CAAC,aAAa,CAAC;IAKpF,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAIhD,UAAU,EAAE,UAAU;IAKzC,QAAQ;IAIF,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"field-metadata.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/field-metadata.subscriber.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAgC,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAGlE,qBACa,uBAAwB,YAAW,yBAAyB,CAAC,aAAa,CAAC;IAKpF,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAIhD,UAAU,EAAE,UAAU;IAKzC,QAAQ;IAIF,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CA6BpE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-metadata.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/field-metadata.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAuE;AACvE,6CAAmD;AAEnD,qCAA2G;AAC3G,6EAAkE;AAI3D,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAGlC,YAEE,UAAuC;QAAtB,eAAU,GAAV,UAAU,CAAY;QAJxB,WAAM,GAAG,IAAI,eAAM,CAAC,yBAAuB,CAAC,IAAI,CAAC,CAAC;QAMjE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,QAAQ;QACN,OAAO,qCAAa,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAiC;
|
|
1
|
+
{"version":3,"file":"field-metadata.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/field-metadata.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAuE;AACvE,6CAAmD;AAEnD,qCAA2G;AAC3G,6EAAkE;AAI3D,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAGlC,YAEE,UAAuC;QAAtB,eAAU,GAAV,UAAU,CAAY;QAJxB,WAAM,GAAG,IAAI,eAAM,CAAC,yBAAuB,CAAC,IAAI,CAAC,CAAC;QAMjE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,QAAQ;QACN,OAAO,qCAAa,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAiC;IA4BnD,CAAC;CACF,CAAA;AA3CY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACU,oBAAU;GAL9B,uBAAuB,CA2CnC","sourcesContent":["import { Injectable, Logger, NotFoundException } from '@nestjs/common';\nimport { InjectDataSource } from '@nestjs/typeorm';\nimport { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';\nimport { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from 'typeorm';\nimport { FieldMetadata } from '../entities/field-metadata.entity';\n\n// @EventSubscriber()\n@Injectable()\nexport class FieldMetadataSubscriber implements EntitySubscriberInterface<FieldMetadata> {\n private readonly logger = new Logger(FieldMetadataSubscriber.name);\n\n constructor(\n @InjectDataSource()\n private readonly dataSource: DataSource,\n ) {\n this.dataSource.subscribers.push(this);\n }\n\n listenTo() {\n return FieldMetadata;\n }\n\n async afterUpdate(event: UpdateEvent<FieldMetadata>): Promise<void> {\n // If a field of type relation.many-to-one is marked for removal, and this field had an inverse relation created \n // It means there is a field of type relation.one-to-many in the co-model, which needs to be removed\n // if (event.entity && event.entity.isMarkedForRemoval && event.entity.type === 'relation' && event.entity.relationType === 'many-to-one' && event.entity.relationCreateInverse === true) {\n // const relationCoModelSingularName = event.entity.relationCoModelSingularName;\n // const relationCoModelFieldName = event.entity.relationCoModelFieldName;\n\n // // Load the field metadata for the co-model.\n // const coModelFieldMetadata = await this.dataSource\n // .getRepository(FieldMetadata)\n // .findOne({\n // where: {\n // name: relationCoModelFieldName,\n // type: 'relation',\n // relationType: 'one-to-many',\n // model: {\n // singularName: relationCoModelSingularName\n // }\n // },\n // });\n\n // // Mark the co-model isMarkedForRemoval field for removal.\n // if (coModelFieldMetadata) {\n // coModelFieldMetadata.isMarkedForRemoval = true;\n // await this.dataSource.getRepository(FieldMetadata).save(coModelFieldMetadata);\n // this.logger.debug(`Marked field ${coModelFieldMetadata.name} in model ${relationCoModelSingularName} for removal.`);\n // }\n // }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled-job.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/scheduled-job.subscriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"scheduled-job.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/scheduled-job.subscriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EACL,UAAU,EAEV,yBAAyB,EACzB,WAAW,EACX,WAAW,EACX,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,qBACa,sBACX,YAAW,yBAAyB,CAAC,YAAY,CAAC;IAW5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,2BAA2B;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAZnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;IAGlE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAIlC;gBAGqC,UAAU,EAAE,UAAU,EAC1C,2BAA2B,EAAE,2BAA2B,EACxD,gBAAgB,EAAE,sBAAsB;IAK3D,QAAQ;IAIF,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC;IAQ5C,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC;IAqB5C,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC;YAIpC,cAAc;YAkDd,cAAc;CA0D7B"}
|
|
@@ -52,7 +52,6 @@ const typeorm_1 = require("@nestjs/typeorm");
|
|
|
52
52
|
const fs = __importStar(require("fs/promises"));
|
|
53
53
|
const module_metadata_entity_1 = require("../entities/module-metadata.entity");
|
|
54
54
|
const scheduled_job_entity_1 = require("../entities/scheduled-job.entity");
|
|
55
|
-
const environment_helper_1 = require("../helpers/environment.helper");
|
|
56
55
|
const module_metadata_helper_service_1 = require("../helpers/module-metadata-helper.service");
|
|
57
56
|
const scheduled_job_repository_1 = require("../repository/scheduled-job.repository");
|
|
58
57
|
const typeorm_2 = require("typeorm");
|
|
@@ -102,7 +101,7 @@ let ScheduledJobSubscriber = ScheduledJobSubscriber_1 = class ScheduledJobSubscr
|
|
|
102
101
|
this.logger.error(`Module metadata not found for scheduled job with ID ${jobEntity?.id}`);
|
|
103
102
|
return;
|
|
104
103
|
}
|
|
105
|
-
const moduleMetadataRepo =
|
|
104
|
+
const moduleMetadataRepo = this.dataSource.getRepository(module_metadata_entity_1.ModuleMetadata);
|
|
106
105
|
const populatedModuleMetadata = await moduleMetadataRepo.findOne({
|
|
107
106
|
where: { id: moduleMetadata.id },
|
|
108
107
|
});
|
|
@@ -151,7 +150,7 @@ let ScheduledJobSubscriber = ScheduledJobSubscriber_1 = class ScheduledJobSubscr
|
|
|
151
150
|
}
|
|
152
151
|
const jobName = jobEntity.scheduleName;
|
|
153
152
|
const existingIndex = metaData.scheduledJobs.findIndex((job) => job.scheduleName === jobName);
|
|
154
|
-
const jobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob
|
|
153
|
+
const jobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob);
|
|
155
154
|
const { moduleId, lastRunAt, nextRunAt, ...dtoToWrite } = jobDto;
|
|
156
155
|
if (existingIndex !== -1) {
|
|
157
156
|
metaData.scheduledJobs[existingIndex] = dtoToWrite;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled-job.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/scheduled-job.subscriber.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport * as fs from \"fs/promises\";\nimport { CreateScheduledJobDto } from \"src/dtos/create-scheduled-job.dto\";\nimport { ModuleMetadata } from \"src/entities/module-metadata.entity\";\nimport { ScheduledJob } from \"src/entities/scheduled-job.entity\";\nimport { isEmbeddedDb } from \"src/helpers/environment.helper\";\nimport { ModuleMetadataHelperService } from \"src/helpers/module-metadata-helper.service\";\nimport { ScheduledJobRepository } from \"src/repository/scheduled-job.repository\";\nimport {\n DataSource,\n EntityManager,\n EntitySubscriberInterface,\n InsertEvent,\n RemoveEvent,\n UpdateEvent,\n} from \"typeorm\";\n\n@Injectable()\nexport class ScheduledJobSubscriber\n implements EntitySubscriberInterface<ScheduledJob> {\n private readonly logger = new Logger(ScheduledJobSubscriber.name);\n\n /** Fields that, when changed (and only these changed), should NOT trigger metadata update. */\n private readonly ignoredUpdateFields: Array<keyof ScheduledJob | string> = [\n \"lastRunAt\",\n \"nextRunAt\",\n \"updatedAt\",\n ];\n\n constructor(\n @InjectDataSource() private readonly dataSource: DataSource,\n private readonly moduleMetadataHelperService: ModuleMetadataHelperService,\n private readonly scheduledJobRepo: ScheduledJobRepository\n ) {\n this.dataSource.subscribers.push(this);\n }\n\n listenTo() {\n return ScheduledJob;\n }\n\n async afterInsert(event: InsertEvent<ScheduledJob>) {\n if (!event.entity) {\n this.logger.debug('No schedule Job entity found in the afterInsert method');\n return;\n }\n await this.updateMetadata(event.entity, event.queryRunner.manager);\n }\n\n async afterUpdate(event: UpdateEvent<ScheduledJob>) {\n if (!event.databaseEntity) {\n this.logger.debug('No schedule Job entity found in the afterUpdate method');\n return;\n }\n\n // get hold of the changed field names \n const changedProps = (event.updatedColumns ?? []).map((c) => c.propertyName);\n\n // Decide whether to skip: only skip when *all* changed fields are in the ignore list\n const onlyIgnoredChanged = changedProps.every((p) => this.ignoredUpdateFields.includes(p));\n\n if (onlyIgnoredChanged) {\n this.logger.debug(`Skipping metadata update for ScheduledJob#${(event.databaseEntity as ScheduledJob).id}; only ignored fields changed: ${changedProps.join(\", \")}`\n );\n return;\n }\n\n await this.updateMetadata(event.databaseEntity, event.queryRunner.manager);\n }\n\n async afterRemove(event: RemoveEvent<ScheduledJob>) {\n await this.removeMetadata(event);\n }\n\n private async removeMetadata(event: RemoveEvent<ScheduledJob>) {\n const jobEntity = event.entity;\n const moduleMetadata = jobEntity?.module;\n\n if (!moduleMetadata) {\n this.logger.error(\n `Module metadata not found for scheduled job with ID ${jobEntity?.id}`\n );\n return;\n }\n\n const moduleMetadataRepo = (isEmbeddedDb() ? event.queryRunner.manager : this.dataSource).getRepository(ModuleMetadata);\n const populatedModuleMetadata = await moduleMetadataRepo.findOne({\n where: { id: moduleMetadata.id },\n });\n\n if (!populatedModuleMetadata) {\n this.logger.error(\n `Could not find ModuleMetadata with ID ${moduleMetadata.id}`\n );\n return;\n }\n const filePath =\n await this.moduleMetadataHelperService.getModuleMetadataFilePath(\n populatedModuleMetadata.name\n );\n try {\n await fs.access(filePath);\n } catch {\n this.logger.error(`Metadata file not found: ${filePath}`);\n return;\n }\n const metaData =\n await this.moduleMetadataHelperService.getModuleMetadataConfiguration(\n filePath\n );\n // Remove, update or insert logic\n const jobName = jobEntity.scheduleName;\n const existingIndex = metaData.scheduledJobs.findIndex(\n (job) => job.scheduleName === jobName\n );\n if (existingIndex !== -1) {\n metaData.scheduledJobs.splice(existingIndex, 1);\n this.logger.log(`Removed scheduled job ${jobName} from metadata`);\n }\n const updatedContent = JSON.stringify(metaData, null, 2);\n await fs.writeFile(filePath, updatedContent);\n this.logger.log(`Updated scheduledJobs in ${filePath}`);\n }\n\n private async updateMetadata(jobEntity: ScheduledJob, entityManager: EntityManager) {\n // populate the job with its relation\n const populatedScheduleJob = await entityManager.findOne(ScheduledJob, {\n where: { id: jobEntity.id },\n relations: ['module'],\n });\n\n if (!populatedScheduleJob) {\n throw new Error(`ScheduleJob not found for id ${jobEntity.id}`);\n }\n const filePath =\n await this.moduleMetadataHelperService.getModuleMetadataFilePath(\n populatedScheduleJob.module?.name\n );\n\n try {\n await fs.access(filePath);\n } catch {\n this.logger.error(`Metadata file not found: ${filePath}`);\n return;\n }\n\n const metaData =\n await this.moduleMetadataHelperService.getModuleMetadataConfiguration(\n filePath\n );\n\n // Ensure scheduledJobs exists\n if (!metaData.scheduledJobs) {\n metaData.scheduledJobs = [];\n }\n\n // Remove, update or insert logic\n const jobName = jobEntity.scheduleName;\n const existingIndex = metaData.scheduledJobs.findIndex(\n (job) => job.scheduleName === jobName\n );\n // Insert or update job in metadata\n const jobDto: CreateScheduledJobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob as ScheduledJob, isEmbeddedDb() ? entityManager : undefined);\n // Strip out bookkeeping/runtime fields before persisting to metadata JSON.\n const {\n moduleId,\n lastRunAt,\n nextRunAt,\n ...dtoToWrite\n } = jobDto;\n if (existingIndex !== -1) {\n metaData.scheduledJobs[existingIndex] = dtoToWrite;\n this.logger.log(`Updated scheduled job ${jobName} in metadata`);\n } else {\n metaData.scheduledJobs.push(dtoToWrite);\n this.logger.log(`Added scheduled job ${jobName} to metadata`);\n }\n\n const updatedContent = JSON.stringify(metaData, null, 2);\n await fs.writeFile(filePath, updatedContent);\n this.logger.log(`Updated scheduledJobs in ${filePath}`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scheduled-job.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/scheduled-job.subscriber.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport * as fs from \"fs/promises\";\nimport { CreateScheduledJobDto } from \"src/dtos/create-scheduled-job.dto\";\nimport { ModuleMetadata } from \"src/entities/module-metadata.entity\";\nimport { ScheduledJob } from \"src/entities/scheduled-job.entity\";\nimport { ModuleMetadataHelperService } from \"src/helpers/module-metadata-helper.service\";\nimport { ScheduledJobRepository } from \"src/repository/scheduled-job.repository\";\nimport {\n DataSource,\n EntityManager,\n EntitySubscriberInterface,\n InsertEvent,\n RemoveEvent,\n UpdateEvent,\n} from \"typeorm\";\n\n@Injectable()\nexport class ScheduledJobSubscriber\n implements EntitySubscriberInterface<ScheduledJob> {\n private readonly logger = new Logger(ScheduledJobSubscriber.name);\n\n /** Fields that, when changed (and only these changed), should NOT trigger metadata update. */\n private readonly ignoredUpdateFields: Array<keyof ScheduledJob | string> = [\n \"lastRunAt\",\n \"nextRunAt\",\n \"updatedAt\",\n ];\n\n constructor(\n @InjectDataSource() private readonly dataSource: DataSource,\n private readonly moduleMetadataHelperService: ModuleMetadataHelperService,\n private readonly scheduledJobRepo: ScheduledJobRepository\n ) {\n this.dataSource.subscribers.push(this);\n }\n\n listenTo() {\n return ScheduledJob;\n }\n\n async afterInsert(event: InsertEvent<ScheduledJob>) {\n if (!event.entity) {\n this.logger.debug('No schedule Job entity found in the afterInsert method');\n return;\n }\n await this.updateMetadata(event.entity, event.queryRunner.manager);\n }\n\n async afterUpdate(event: UpdateEvent<ScheduledJob>) {\n if (!event.databaseEntity) {\n this.logger.debug('No schedule Job entity found in the afterUpdate method');\n return;\n }\n\n // get hold of the changed field names \n const changedProps = (event.updatedColumns ?? []).map((c) => c.propertyName);\n\n // Decide whether to skip: only skip when *all* changed fields are in the ignore list\n const onlyIgnoredChanged = changedProps.every((p) => this.ignoredUpdateFields.includes(p));\n\n if (onlyIgnoredChanged) {\n this.logger.debug(`Skipping metadata update for ScheduledJob#${(event.databaseEntity as ScheduledJob).id}; only ignored fields changed: ${changedProps.join(\", \")}`\n );\n return;\n }\n\n await this.updateMetadata(event.databaseEntity, event.queryRunner.manager);\n }\n\n async afterRemove(event: RemoveEvent<ScheduledJob>) {\n await this.removeMetadata(event);\n }\n\n private async removeMetadata(event: RemoveEvent<ScheduledJob>) {\n const jobEntity = event.entity;\n const moduleMetadata = jobEntity?.module;\n\n if (!moduleMetadata) {\n this.logger.error(\n `Module metadata not found for scheduled job with ID ${jobEntity?.id}`\n );\n return;\n }\n\n const moduleMetadataRepo = this.dataSource.getRepository(ModuleMetadata);\n const populatedModuleMetadata = await moduleMetadataRepo.findOne({\n where: { id: moduleMetadata.id },\n });\n\n if (!populatedModuleMetadata) {\n this.logger.error(\n `Could not find ModuleMetadata with ID ${moduleMetadata.id}`\n );\n return;\n }\n const filePath =\n await this.moduleMetadataHelperService.getModuleMetadataFilePath(\n populatedModuleMetadata.name\n );\n try {\n await fs.access(filePath);\n } catch {\n this.logger.error(`Metadata file not found: ${filePath}`);\n return;\n }\n const metaData =\n await this.moduleMetadataHelperService.getModuleMetadataConfiguration(\n filePath\n );\n // Remove, update or insert logic\n const jobName = jobEntity.scheduleName;\n const existingIndex = metaData.scheduledJobs.findIndex(\n (job) => job.scheduleName === jobName\n );\n if (existingIndex !== -1) {\n metaData.scheduledJobs.splice(existingIndex, 1);\n this.logger.log(`Removed scheduled job ${jobName} from metadata`);\n }\n const updatedContent = JSON.stringify(metaData, null, 2);\n await fs.writeFile(filePath, updatedContent);\n this.logger.log(`Updated scheduledJobs in ${filePath}`);\n }\n\n private async updateMetadata(jobEntity: ScheduledJob, entityManager: EntityManager) {\n // populate the job with its relation\n const populatedScheduleJob = await entityManager.findOne(ScheduledJob, {\n where: { id: jobEntity.id },\n relations: ['module'],\n });\n\n if (!populatedScheduleJob) {\n throw new Error(`ScheduleJob not found for id ${jobEntity.id}`);\n }\n const filePath =\n await this.moduleMetadataHelperService.getModuleMetadataFilePath(\n populatedScheduleJob.module?.name\n );\n\n try {\n await fs.access(filePath);\n } catch {\n this.logger.error(`Metadata file not found: ${filePath}`);\n return;\n }\n\n const metaData =\n await this.moduleMetadataHelperService.getModuleMetadataConfiguration(\n filePath\n );\n\n // Ensure scheduledJobs exists\n if (!metaData.scheduledJobs) {\n metaData.scheduledJobs = [];\n }\n\n // Remove, update or insert logic\n const jobName = jobEntity.scheduleName;\n const existingIndex = metaData.scheduledJobs.findIndex(\n (job) => job.scheduleName === jobName\n );\n // Insert or update job in metadata\n const jobDto: CreateScheduledJobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob as ScheduledJob);\n // Strip out bookkeeping/runtime fields before persisting to metadata JSON.\n const {\n moduleId,\n lastRunAt,\n nextRunAt,\n ...dtoToWrite\n } = jobDto;\n if (existingIndex !== -1) {\n metaData.scheduledJobs[existingIndex] = dtoToWrite;\n this.logger.log(`Updated scheduled job ${jobName} in metadata`);\n } else {\n metaData.scheduledJobs.push(dtoToWrite);\n this.logger.log(`Added scheduled job ${jobName} to metadata`);\n }\n\n const updatedContent = JSON.stringify(metaData, null, 2);\n await fs.writeFile(filePath, updatedContent);\n this.logger.log(`Updated scheduledJobs in ${filePath}`);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-rule.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/security-rule.subscriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"security-rule.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/security-rule.subscriber.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1F,qBACa,sBAAuB,YAAW,yBAAyB,CAAC,YAAY,CAAC;IAI9E,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,QAAQ,CAAC,2BAA2B,EAAE,2BAA2B;IACjE,QAAQ,CAAC,gBAAgB,EAAE,sBAAsB;IALrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;gBAG7C,UAAU,EAAE,UAAU,EAC9B,2BAA2B,EAAE,2BAA2B,EACxD,gBAAgB,EAAE,sBAAsB;IAKrD,QAAQ;IAIF,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC;IAI5C,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC;IAI5C,iBAAiB,CAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,WAAW,CAAC,YAAY,CAAC;CAiDvF"}
|
|
@@ -53,7 +53,6 @@ const fs = __importStar(require("fs/promises"));
|
|
|
53
53
|
const constants_1 = require("../constants");
|
|
54
54
|
const model_metadata_entity_1 = require("../entities/model-metadata.entity");
|
|
55
55
|
const security_rule_entity_1 = require("../entities/security-rule.entity");
|
|
56
|
-
const environment_helper_1 = require("../helpers/environment.helper");
|
|
57
56
|
const module_metadata_helper_service_1 = require("../helpers/module-metadata-helper.service");
|
|
58
57
|
const security_rule_repository_1 = require("../repository/security-rule.repository");
|
|
59
58
|
const typeorm_2 = require("typeorm");
|
|
@@ -81,8 +80,7 @@ let SecurityRuleSubscriber = SecurityRuleSubscriber_1 = class SecurityRuleSubscr
|
|
|
81
80
|
this.logger.error(`Model metadata not found for security rule with id ${event.entity.id}`);
|
|
82
81
|
return;
|
|
83
82
|
}
|
|
84
|
-
const
|
|
85
|
-
const modelMetadataRepo = mgr.getRepository(model_metadata_entity_1.ModelMetadata);
|
|
83
|
+
const modelMetadataRepo = this.dataSource.getRepository(model_metadata_entity_1.ModelMetadata);
|
|
86
84
|
const populatedModelMetadata = await modelMetadataRepo.findOne({
|
|
87
85
|
where: {
|
|
88
86
|
id: modelMetadata.id
|
|
@@ -105,12 +103,12 @@ let SecurityRuleSubscriber = SecurityRuleSubscriber_1 = class SecurityRuleSubscr
|
|
|
105
103
|
const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
|
|
106
104
|
if (metaData.securityRules) {
|
|
107
105
|
const securityRuleIndex = metaData.securityRules?.findIndex((ruleFromFile) => ruleFromFile.name === securityRule.name);
|
|
108
|
-
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule
|
|
106
|
+
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule);
|
|
109
107
|
metaData.securityRules[securityRuleIndex] = { ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) };
|
|
110
108
|
}
|
|
111
109
|
else {
|
|
112
110
|
const securityRules = [];
|
|
113
|
-
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule
|
|
111
|
+
const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule);
|
|
114
112
|
securityRules.push({ ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) });
|
|
115
113
|
metaData.securityRules = securityRules;
|
|
116
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-rule.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/security-rule.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,6CAAmD;AACnD,gDAAkC;AAClC,4CAAuD;AACvD,6EAAmE;AACnE,2EAAiE;AACjE,
|
|
1
|
+
{"version":3,"file":"security-rule.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/security-rule.subscriber.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from '@nestjs/common';\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport * as fs from 'fs/promises'; // Use the Promise-based version of fs for async/await\nimport { SOLID_CORE_MODULE_NAME } from 'src/constants';\nimport { ModelMetadata } from 'src/entities/model-metadata.entity';\nimport { SecurityRule } from 'src/entities/security-rule.entity';\nimport { ModuleMetadataHelperService } from \"src/helpers/module-metadata-helper.service\";\nimport { SecurityRuleRepository } from 'src/repository/security-rule.repository';\nimport { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from \"typeorm\";\n\n@Injectable()\nexport class SecurityRuleSubscriber implements EntitySubscriberInterface<SecurityRule> {\n private readonly logger = new Logger(SecurityRuleSubscriber.name);\n constructor(\n @InjectDataSource()\n private readonly dataSource: DataSource,\n readonly moduleMetadataHelperService: ModuleMetadataHelperService,\n readonly securityRuleRepo: SecurityRuleRepository,\n ) {\n this.dataSource.subscribers.push(this);\n }\n\n listenTo() {\n return SecurityRule;\n }\n\n async afterInsert(event: InsertEvent<SecurityRule>) {\n await this.saveSecurityRules(event);\n }\n\n async afterUpdate(event: UpdateEvent<SecurityRule>) {\n await this.saveSecurityRules(event);\n }\n\n async saveSecurityRules(event: UpdateEvent<SecurityRule> | InsertEvent<SecurityRule>) {\n const securityRule = event.entity as SecurityRule;\n const modelMetadata = event.entity.modelMetadata;\n if (!modelMetadata) {\n this.logger.error(`Model metadata not found for security rule with id ${event.entity.id}`);\n return;\n }\n\n const modelMetadataRepo = this.dataSource.getRepository(ModelMetadata);\n const populatedModelMetadata = await modelMetadataRepo.findOne({\n where: {\n id: modelMetadata.id\n },\n relations: {\n module: true,\n }\n });\n\n // Ignore further processing if the module is solid core module, since solid core security rules cannot be seeded from the UI\n if (populatedModelMetadata.module.name === SOLID_CORE_MODULE_NAME) {\n return;\n }\n\n const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(populatedModelMetadata.module.name);\n try {\n await fs.access(filePath);\n } catch (error: any) {\n // FIXME - Should we actually delete the security rule here, if the file is not found?\n this.logger.error(`File not found at path: ${filePath}`);\n return;\n }\n const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);\n\n if (metaData.securityRules) {\n const securityRuleIndex = metaData.securityRules?.findIndex((ruleFromFile: { name: string }) => ruleFromFile.name === securityRule.name);\n const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule)\n metaData.securityRules[securityRuleIndex] = { ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) }\n }\n else {\n const securityRules = []\n const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule)\n securityRules.push({ ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) })\n metaData.securityRules = securityRules\n }\n // Write the updated object back to the file\n const updatedContent = JSON.stringify(metaData, null, 2);\n await fs.writeFile(filePath, updatedContent);\n }\n\n}"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidxai/core",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.13",
|
|
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",
|
|
@@ -47,7 +47,7 @@ export class MediaController {
|
|
|
47
47
|
return this.service.update(id, updateDto, files, true);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
@Public()
|
|
50
|
+
// @Public()
|
|
51
51
|
// @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
|
|
52
52
|
@ApiBearerAuth("jwt")
|
|
53
53
|
@Post('/upload')
|
|
@@ -13,15 +13,4 @@ export function isRedisConfigured(configService: ConfigService): boolean {
|
|
|
13
13
|
const host = configService.get<string>('REDIS_HOST');
|
|
14
14
|
const port = configService.get<string>('REDIS_PORT');
|
|
15
15
|
return host && port && isNumber(parseInt(port));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Returns true when the project is configured to use an embedded PGlite
|
|
20
|
-
* database with a single-connection pool. Several TypeORM subscriber and
|
|
21
|
-
* code-generation workarounds are only necessary (and only applied) under
|
|
22
|
-
* this constraint; on a regular multi-connection Postgres pool the original
|
|
23
|
-
* behaviour is preserved.
|
|
24
|
-
*/
|
|
25
|
-
export function isEmbeddedDb(): boolean {
|
|
26
|
-
return process.env.DEFAULT_DATABASE_DRIVER === 'pglite';
|
|
27
16
|
}
|
|
@@ -4,10 +4,8 @@ import { forwardRef, Inject, Injectable, Logger } from "@nestjs/common";
|
|
|
4
4
|
import { _ } from "lodash";
|
|
5
5
|
import { LEGACY_TABLE_FIELDS_PREFIX } from "src/entities/legacy-common.entity-with-existing-id.entity";
|
|
6
6
|
import { LegacyTableType } from "src/enums/legacy-table-type.enum";
|
|
7
|
-
import { ModelMetadata } from "src/entities/model-metadata.entity";
|
|
8
7
|
import { ModelMetadataRepository } from "src/repository/model-metadata.repository";
|
|
9
8
|
import { SolidRegistry } from "./solid-registry";
|
|
10
|
-
import { EntityManager } from "typeorm";
|
|
11
9
|
|
|
12
10
|
@Injectable()
|
|
13
11
|
export class ModelMetadataHelperService {
|
|
@@ -145,11 +143,8 @@ export class ModelMetadataHelperService {
|
|
|
145
143
|
return systemFieldsMetadata;
|
|
146
144
|
}
|
|
147
145
|
|
|
148
|
-
async loadFieldHierarchy(modelName: any
|
|
149
|
-
const
|
|
150
|
-
? manager.getRepository(ModelMetadata)
|
|
151
|
-
: this.modelMetadataRepo;
|
|
152
|
-
const model = await repo.findOne({
|
|
146
|
+
async loadFieldHierarchy(modelName: any) {
|
|
147
|
+
const model = await this.modelMetadataRepo.findOne({
|
|
153
148
|
where: {
|
|
154
149
|
singularName: modelName,
|
|
155
150
|
},
|
|
@@ -34,7 +34,7 @@ export class ModuleMetadataHelperService {
|
|
|
34
34
|
if (!moduleName) {
|
|
35
35
|
return '';
|
|
36
36
|
}
|
|
37
|
-
return path.resolve(process.cwd(), 'src', moduleName);
|
|
37
|
+
return path.resolve(process.cwd(), 'src', kebabCase(moduleName));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
async getSolidUiModulePath(moduleName: string): Promise<string> {
|
|
@@ -8,7 +8,7 @@ import { Locale } from 'src/entities/locale.entity';
|
|
|
8
8
|
import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
9
9
|
import { IScheduledJob } from 'src/services/scheduled-jobs/scheduled-job.interface';
|
|
10
10
|
import { IDashboardWidgetDataProvider, IErrorCodeProvider, ISecurityRuleConfigProvider, ISelectionProvider, ISelectionProviderContext, ISolidDatabaseModule } from "../interfaces";
|
|
11
|
-
import {
|
|
11
|
+
import { ObjectLiteral } from 'typeorm';
|
|
12
12
|
|
|
13
13
|
type ControllerMetadata = {
|
|
14
14
|
name: string;
|
|
@@ -49,12 +49,6 @@ export interface TypeOrmEventContext {
|
|
|
49
49
|
metadataName?: string;
|
|
50
50
|
updatedColumns?: string[];
|
|
51
51
|
updatedRelations?: string[];
|
|
52
|
-
// Transaction-bound EntityManager from the originating TypeORM event. Providers that
|
|
53
|
-
// need to query the DB during a pre-compute hook must use this (not an injected
|
|
54
|
-
// DataSource/EntityManager) so the query runs on the active transaction's connection.
|
|
55
|
-
// On single-threaded embedded engines (PGlite), a second connection mid-transaction
|
|
56
|
-
// can deadlock.
|
|
57
|
-
manager?: EntityManager;
|
|
58
52
|
}
|
|
59
53
|
|
|
60
54
|
export interface ComputedFieldMetadata<TContext = any> {
|
package/src/index.ts
CHANGED
|
@@ -171,7 +171,6 @@ export * from './guards/permissions.guard'
|
|
|
171
171
|
export * from './guards/roles.guard'
|
|
172
172
|
|
|
173
173
|
export * from './helpers/command.service'
|
|
174
|
-
export * from './helpers/nodemon-heartbeat'
|
|
175
174
|
export * from './helpers/module.helper'
|
|
176
175
|
export * from './helpers/schematic.service'
|
|
177
176
|
export * from './helpers/solid-registry'
|
|
@@ -3,7 +3,7 @@ import { CreateScheduledJobDto } from "src/dtos/create-scheduled-job.dto";
|
|
|
3
3
|
import { ModuleMetadata } from "src/entities/module-metadata.entity";
|
|
4
4
|
import { ScheduledJob } from "src/entities/scheduled-job.entity";
|
|
5
5
|
import { RequestContextService } from "src/services/request-context.service";
|
|
6
|
-
import { DataSource
|
|
6
|
+
import { DataSource } from "typeorm";
|
|
7
7
|
import { SecurityRuleRepository } from "./security-rule.repository";
|
|
8
8
|
import { SolidBaseRepository } from "./solid-base.repository";
|
|
9
9
|
|
|
@@ -101,25 +101,19 @@ export class ScheduledJobRepository extends SolidBaseRepository<ScheduledJob> {
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* Converts an entity to a plain DTO object.
|
|
104
|
-
* When manager is provided (e.g. from a TypeORM subscriber event), the
|
|
105
|
-
* fallback relation load uses it so the query runs on the active
|
|
106
|
-
* transaction's connection instead of the default DataSource pool.
|
|
107
104
|
*/
|
|
108
|
-
async toDto(scheduledJob: ScheduledJob
|
|
105
|
+
async toDto(scheduledJob: ScheduledJob): Promise<CreateScheduledJobDto> {
|
|
109
106
|
let populatedScheduledJob: ScheduledJob = scheduledJob;
|
|
110
107
|
// If the scheduled job does not have the module relation loaded, load it
|
|
111
108
|
if (!scheduledJob.module) {
|
|
112
|
-
|
|
113
|
-
? manager.getRepository(ScheduledJob)
|
|
114
|
-
: this;
|
|
115
|
-
populatedScheduledJob = await repo.findOne({
|
|
109
|
+
populatedScheduledJob = await this.findOne({
|
|
116
110
|
where: {
|
|
117
111
|
id: scheduledJob.id,
|
|
118
112
|
},
|
|
119
113
|
relations: {
|
|
120
114
|
module: true,
|
|
121
115
|
},
|
|
122
|
-
})
|
|
116
|
+
});
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
if (!populatedScheduledJob.module) {
|
|
@@ -9,7 +9,7 @@ import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
|
9
9
|
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
10
10
|
import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
|
|
11
11
|
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
|
-
import { Brackets, DataSource,
|
|
12
|
+
import { Brackets, DataSource, SelectQueryBuilder } from 'typeorm';
|
|
13
13
|
import { SolidBaseRepository } from './solid-base.repository';
|
|
14
14
|
|
|
15
15
|
@Injectable()
|
|
@@ -84,18 +84,12 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
|
|
|
84
84
|
return configString.replace('$activeUserId', activeUser.sub.toString());
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
async toDto(securityRule: SecurityRule
|
|
87
|
+
async toDto(securityRule: SecurityRule): Promise<UpdateSecurityRuleDto> {
|
|
88
88
|
// load the role and model relations for the security rule
|
|
89
89
|
let populatedSecurityRule: SecurityRule = securityRule;
|
|
90
90
|
// If the security rule does not have the role and model relations loaded, load them
|
|
91
91
|
if (!securityRule.role || !securityRule.modelMetadata) {
|
|
92
|
-
|
|
93
|
-
// use it so the query runs on the active transaction's connection
|
|
94
|
-
// instead of trying to acquire a second connection from the pool.
|
|
95
|
-
const repo = manager
|
|
96
|
-
? manager.getRepository(SecurityRule)
|
|
97
|
-
: this;
|
|
98
|
-
populatedSecurityRule = await repo.findOne({
|
|
92
|
+
populatedSecurityRule = await this.findOne({
|
|
99
93
|
where: {
|
|
100
94
|
id: securityRule.id,
|
|
101
95
|
},
|
|
@@ -103,7 +97,7 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
|
|
|
103
97
|
role: true,
|
|
104
98
|
modelMetadata: true,
|
|
105
99
|
},
|
|
106
|
-
})
|
|
100
|
+
});
|
|
107
101
|
}
|
|
108
102
|
|
|
109
103
|
return {
|
package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
|
|
|
2
2
|
import { InjectEntityManager } from '@nestjs/typeorm';
|
|
3
3
|
import { ComputedFieldProvider } from 'src/decorators/computed-field-provider.decorator';
|
|
4
4
|
import { CommonEntity } from 'src/entities/common.entity';
|
|
5
|
-
import { isEmbeddedDb } from 'src/helpers/environment.helper';
|
|
6
5
|
import { ComputedFieldMetadata } from 'src/helpers/solid-registry';
|
|
7
6
|
import { IEntityPreComputeFieldProvider } from 'src/interfaces';
|
|
8
7
|
import { EntityManager } from 'typeorm';
|
|
@@ -46,12 +45,7 @@ export class AlphaNumExternalIdComputationProvider<T extends CommonEntity> imple
|
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
// so the uniqueness check runs on the active connection. On regular Postgres, use the
|
|
51
|
-
// injected EntityManager (original behaviour).
|
|
52
|
-
const manager = isEmbeddedDb() ? (eventContext?.manager ?? this.entityManager) : this.entityManager;
|
|
53
|
-
|
|
54
|
-
const uniqueCode = await this.generateUniqueExternalId(manager, resolvedPrefix, codeLength, computedFieldMetadata.fieldName, entityName);
|
|
48
|
+
const uniqueCode = await this.generateUniqueExternalId(resolvedPrefix, codeLength, computedFieldMetadata.fieldName, entityName);
|
|
55
49
|
const finalExternalId = resolvedPrefix ? `${resolvedPrefix}-${uniqueCode}` : uniqueCode;
|
|
56
50
|
|
|
57
51
|
triggerEntity[computedFieldMetadata.fieldName] = finalExternalId;
|
|
@@ -66,8 +60,8 @@ export class AlphaNumExternalIdComputationProvider<T extends CommonEntity> imple
|
|
|
66
60
|
return result;
|
|
67
61
|
}
|
|
68
62
|
|
|
69
|
-
private async isExternalIdUnique(
|
|
70
|
-
const count = await
|
|
63
|
+
private async isExternalIdUnique(externalId: string, fieldName: string, entityName: string): Promise<boolean> {
|
|
64
|
+
const count = await this.entityManager.count(entityName as any,
|
|
71
65
|
{
|
|
72
66
|
where: { [fieldName]: externalId },
|
|
73
67
|
}
|
|
@@ -75,7 +69,7 @@ export class AlphaNumExternalIdComputationProvider<T extends CommonEntity> imple
|
|
|
75
69
|
return count === 0;
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
private async generateUniqueExternalId(
|
|
72
|
+
private async generateUniqueExternalId(resolvedPrefix: string, codeLength: number, fieldName: string, entityName: string): Promise<string> {
|
|
79
73
|
const maxAttempts = 10;
|
|
80
74
|
|
|
81
75
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
@@ -84,7 +78,7 @@ export class AlphaNumExternalIdComputationProvider<T extends CommonEntity> imple
|
|
|
84
78
|
|
|
85
79
|
const fullId = resolvedPrefix ? `${resolvedPrefix}-${newId}` : newId;
|
|
86
80
|
|
|
87
|
-
const isUnique = await this.isExternalIdUnique(
|
|
81
|
+
const isUnique = await this.isExternalIdUnique(fullId, fieldName, entityName);
|
|
88
82
|
|
|
89
83
|
if (isUnique) {
|
|
90
84
|
return newId;
|
|
@@ -293,9 +293,13 @@ export class CrudHelperService {
|
|
|
293
293
|
const orderOptions = this.orderOptions(normalizedSort);
|
|
294
294
|
if (orderOptions) {
|
|
295
295
|
const orderOptionKeys = Object.keys(orderOptions) as Array<keyof typeof orderOptions>;
|
|
296
|
+
let hasExplicitIdSort = false;
|
|
296
297
|
orderOptionKeys.forEach((key) => {
|
|
297
298
|
const value = orderOptions[key] as 'ASC' | 'DESC';
|
|
298
299
|
const field = String(key);
|
|
300
|
+
if (field === 'id') {
|
|
301
|
+
hasExplicitIdSort = true;
|
|
302
|
+
}
|
|
299
303
|
if (field.includes('.')) {
|
|
300
304
|
const { alias, property, created } = this.ensureRelationPathJoined(qb, entityAlias, field.split('.'));
|
|
301
305
|
const orderColumn = `${alias}.${property}`;
|
|
@@ -305,6 +309,9 @@ export class CrudHelperService {
|
|
|
305
309
|
qb.addOrderBy(`${entityAlias}.${field}`, value);
|
|
306
310
|
}
|
|
307
311
|
});
|
|
312
|
+
if (!hasExplicitIdSort) {
|
|
313
|
+
qb.addOrderBy(`${entityAlias}.id`, 'DESC');
|
|
314
|
+
}
|
|
308
315
|
}
|
|
309
316
|
}
|
|
310
317
|
|
|
@@ -405,10 +405,7 @@ export class DatasourceIntrospectionService {
|
|
|
405
405
|
throw new BadRequestException("Persist the model mapping first so the generated entity can be registered before creating migration artifacts.");
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
|
|
409
|
-
await fs.mkdir(path.dirname(plan.migration.filePath), { recursive: true });
|
|
410
|
-
await fs.writeFile(plan.migration.filePath, plan.migration.content, "utf8");
|
|
411
|
-
}
|
|
408
|
+
const migrationWritten = await this.writeMigrationFile(plan.migration);
|
|
412
409
|
|
|
413
410
|
const datasourceFile = await this.syncTypeormDatasourceFile(
|
|
414
411
|
module.name,
|
|
@@ -422,7 +419,7 @@ export class DatasourceIntrospectionService {
|
|
|
422
419
|
operation: plan.operation,
|
|
423
420
|
migration: {
|
|
424
421
|
...plan.migration,
|
|
425
|
-
written:
|
|
422
|
+
written: migrationWritten,
|
|
426
423
|
},
|
|
427
424
|
datasourceFile,
|
|
428
425
|
summary: plan.summary,
|
|
@@ -1918,6 +1915,22 @@ export class DatasourceIntrospectionService {
|
|
|
1918
1915
|
};
|
|
1919
1916
|
}
|
|
1920
1917
|
|
|
1918
|
+
private async writeMigrationFile(migration: MappingPreviewRecord["migration"]) {
|
|
1919
|
+
if (!migration.willGenerate || !migration.content || !migration.filePath) {
|
|
1920
|
+
return false;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
await fs.mkdir(path.dirname(migration.filePath), { recursive: true });
|
|
1924
|
+
await fs.writeFile(migration.filePath, migration.content, "utf8");
|
|
1925
|
+
|
|
1926
|
+
const writtenContent = await fs.readFile(migration.filePath, "utf8").catch(() => null);
|
|
1927
|
+
if (writtenContent !== migration.content) {
|
|
1928
|
+
throw new BadRequestException(`Migration file could not be verified after writing: ${migration.filePath}`);
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
return true;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1921
1934
|
private renderTypeormDatasourceTemplate(input: {
|
|
1922
1935
|
datasource: DatasourceRecord;
|
|
1923
1936
|
entityClassName: string;
|
|
@@ -28,8 +28,6 @@ import { PermissionMetadata } from '../entities/permission-metadata.entity';
|
|
|
28
28
|
import { RoleMetadata } from '../entities/role-metadata.entity';
|
|
29
29
|
import { ViewMetadata } from '../entities/view-metadata.entity';
|
|
30
30
|
import { CommandService } from '../helpers/command.service';
|
|
31
|
-
import { isEmbeddedDb } from '../helpers/environment.helper';
|
|
32
|
-
import { startNodemonHeartbeat } from '../helpers/nodemon-heartbeat';
|
|
33
31
|
import {
|
|
34
32
|
REFRESH_MODEL_COMMAND,
|
|
35
33
|
REMOVE_FIELDS_COMMAND,
|
|
@@ -1032,26 +1030,6 @@ export class ModelMetadataService {
|
|
|
1032
1030
|
@DisallowInProduction()
|
|
1033
1031
|
async generateCodeViaCtl(modelId: number): Promise<string> {
|
|
1034
1032
|
const model = await this.findOne(modelId);
|
|
1035
|
-
const isEmbedded = isEmbeddedDb();
|
|
1036
|
-
|
|
1037
|
-
// When using an embedded PGlite database, the single-connection limit means
|
|
1038
|
-
// a spawned `solid refresh-model` subprocess cannot get its own DB connection
|
|
1039
|
-
// while the API server is already holding it. Fall back to in-process code
|
|
1040
|
-
// generation (which reuses the existing connection).
|
|
1041
|
-
if (isEmbedded) {
|
|
1042
|
-
this.logger.log('Embedded database detected — generating API code in-process');
|
|
1043
|
-
// Prevent nodemon from restarting the server while schematics write files.
|
|
1044
|
-
const heartbeat = startNodemonHeartbeat(path.join(process.cwd(), 'src'));
|
|
1045
|
-
try {
|
|
1046
|
-
return await this.handleGenerateCode({
|
|
1047
|
-
modelUserKey: model.singularName,
|
|
1048
|
-
dryRun: false,
|
|
1049
|
-
});
|
|
1050
|
-
} finally {
|
|
1051
|
-
heartbeat.stop();
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
1033
|
return this.commandService.executeCommandWithArgs({
|
|
1056
1034
|
command: 'npx',
|
|
1057
1035
|
args: ['@solidxai/solidctl@latest', 'generate', 'model', `--name=${model.singularName}`],
|
|
@@ -26,8 +26,6 @@ import {
|
|
|
26
26
|
} from '../helpers/schematic.service';
|
|
27
27
|
import { CommandService } from '../helpers/command.service';
|
|
28
28
|
import { SolidRegistry } from '../helpers/solid-registry';
|
|
29
|
-
import { isEmbeddedDb } from '../helpers/environment.helper';
|
|
30
|
-
import { startNodemonHeartbeat } from '../helpers/nodemon-heartbeat';
|
|
31
29
|
import { CodeGenerationOptions, ModuleMetadataConfiguration } from '../interfaces';
|
|
32
30
|
import { CrudHelperService } from './crud-helper.service';
|
|
33
31
|
import { ModelMetadataService } from './model-metadata.service';
|
|
@@ -536,40 +534,6 @@ export class ModuleMetadataService {
|
|
|
536
534
|
@DisallowInProduction()
|
|
537
535
|
async generateCodeViaCtl(moduleId: number): Promise<string> {
|
|
538
536
|
const module = await this.findOne(moduleId);
|
|
539
|
-
const isEmbedded = isEmbeddedDb();
|
|
540
|
-
|
|
541
|
-
// When using an embedded PGlite database, the single-connection limit means
|
|
542
|
-
// a spawned `solid refresh-module` subprocess cannot get its own DB connection
|
|
543
|
-
// while the API server is already holding it. Fall back to in-process code
|
|
544
|
-
// generation (which reuses the existing connection) and only spawn solidctl
|
|
545
|
-
// for the UI module scaffold (which does not need a database connection).
|
|
546
|
-
if (isEmbedded) {
|
|
547
|
-
this.logger.log('Embedded database detected — generating API code in-process');
|
|
548
|
-
// Prevent nodemon from restarting the server while schematics write files.
|
|
549
|
-
// The heartbeat touches a watched file every 1.5 s (nodemon delay is 2 s),
|
|
550
|
-
// keeping the delay from expiring during DB queries between schematic runs.
|
|
551
|
-
const heartbeat = startNodemonHeartbeat(path.join(process.cwd(), 'src'));
|
|
552
|
-
try {
|
|
553
|
-
const apiOutput = await this.generateCode({
|
|
554
|
-
moduleUserKey: module.name,
|
|
555
|
-
dryRun: false,
|
|
556
|
-
});
|
|
557
|
-
let uiOutput = '';
|
|
558
|
-
try {
|
|
559
|
-
uiOutput = await this.commandService.executeCommandWithArgs({
|
|
560
|
-
command: 'npx',
|
|
561
|
-
args: ['@solidxai/solidctl@latest', 'generate', 'ui-module', `--name=${module.name}`],
|
|
562
|
-
cwd: path.join(process.cwd(), '..'),
|
|
563
|
-
});
|
|
564
|
-
} catch (error) {
|
|
565
|
-
this.logger.warn('UI module scaffold failed (solidctl may be outdated); API code was generated successfully.');
|
|
566
|
-
}
|
|
567
|
-
return `${apiOutput}\n${uiOutput}`;
|
|
568
|
-
} finally {
|
|
569
|
-
heartbeat.stop();
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
537
|
return this.commandService.executeCommandWithArgs({
|
|
574
538
|
command: 'npx',
|
|
575
539
|
args: ['@solidxai/solidctl@latest', 'generate', 'module', `--name=${module.name}`],
|