@solidxai/core 0.1.13-beta.7 → 0.1.13-beta.8
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/.claude/settings.local.json +16 -0
- package/CHANGELOG.md +548 -0
- package/CLAUDE.md +26 -0
- package/dist/constants/draft-publish-fields.d.ts +2 -0
- package/dist/constants/draft-publish-fields.d.ts.map +1 -0
- package/dist/constants/draft-publish-fields.js +5 -0
- package/dist/constants/draft-publish-fields.js.map +1 -0
- package/dist/constants/media-file-types.d.ts +9 -0
- package/dist/constants/media-file-types.d.ts.map +1 -1
- package/dist/constants/media-file-types.js +27 -1
- package/dist/constants/media-file-types.js.map +1 -1
- package/dist/controllers/action-metadata.controller.d.ts +1 -1
- package/dist/controllers/action-metadata.controller.js +1 -1
- package/dist/controllers/agent-event.controller.d.ts +1 -1
- package/dist/controllers/agent-event.controller.js +1 -1
- package/dist/controllers/agent-session.controller.d.ts +1 -1
- package/dist/controllers/agent-session.controller.js +1 -1
- package/dist/controllers/dashboard-user-layout.controller.d.ts +1 -1
- package/dist/controllers/dashboard-user-layout.controller.js +1 -1
- package/dist/controllers/email-template.controller.d.ts +1 -1
- package/dist/controllers/email-template.controller.js +1 -1
- package/dist/controllers/export-template.controller.d.ts +1 -1
- package/dist/controllers/export-template.controller.d.ts.map +1 -1
- package/dist/controllers/export-template.controller.js +6 -4
- package/dist/controllers/export-template.controller.js.map +1 -1
- package/dist/controllers/export-transaction.controller.d.ts +1 -1
- package/dist/controllers/export-transaction.controller.js +1 -1
- package/dist/controllers/import-transaction-error-log.controller.d.ts +1 -1
- package/dist/controllers/import-transaction-error-log.controller.js +1 -1
- package/dist/controllers/import-transaction.controller.d.ts +1 -1
- package/dist/controllers/import-transaction.controller.d.ts.map +1 -1
- package/dist/controllers/import-transaction.controller.js +7 -7
- package/dist/controllers/import-transaction.controller.js.map +1 -1
- package/dist/controllers/list-of-values.controller.d.ts +1 -1
- package/dist/controllers/list-of-values.controller.js +1 -1
- package/dist/controllers/locale.controller.d.ts +1 -1
- package/dist/controllers/locale.controller.js +1 -1
- package/dist/controllers/mcp-audit-log.controller.d.ts +1 -1
- package/dist/controllers/mcp-audit-log.controller.js +1 -1
- package/dist/controllers/media.controller.d.ts +1 -1
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +12 -7
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/controllers/menu-item-metadata.controller.d.ts +1 -1
- package/dist/controllers/menu-item-metadata.controller.js +1 -1
- package/dist/controllers/model-sequence.controller.d.ts +1 -1
- package/dist/controllers/model-sequence.controller.js +1 -1
- package/dist/controllers/module-package.controller.d.ts.map +1 -1
- package/dist/controllers/module-package.controller.js +2 -3
- package/dist/controllers/module-package.controller.js.map +1 -1
- package/dist/controllers/mq-message-queue.controller.d.ts +1 -1
- package/dist/controllers/mq-message-queue.controller.js +1 -1
- package/dist/controllers/mq-message.controller.d.ts +1 -1
- package/dist/controllers/mq-message.controller.js +1 -1
- package/dist/controllers/permission-metadata.controller.d.ts +1 -1
- package/dist/controllers/permission-metadata.controller.js +1 -1
- package/dist/controllers/role-metadata.controller.d.ts +1 -1
- package/dist/controllers/role-metadata.controller.js +1 -1
- package/dist/controllers/saved-filters.controller.d.ts +1 -1
- package/dist/controllers/saved-filters.controller.js +1 -1
- package/dist/controllers/scheduled-job.controller.d.ts +1 -1
- package/dist/controllers/scheduled-job.controller.js +1 -1
- package/dist/controllers/security-rule.controller.d.ts +1 -1
- package/dist/controllers/security-rule.controller.js +1 -1
- package/dist/controllers/sms-template.controller.d.ts +1 -1
- package/dist/controllers/sms-template.controller.js +1 -1
- package/dist/controllers/user-activity-history.controller.d.ts +1 -1
- package/dist/controllers/user-activity-history.controller.js +1 -1
- package/dist/controllers/user-view-metadata.controller.d.ts +1 -1
- package/dist/controllers/user-view-metadata.controller.js +1 -1
- package/dist/controllers/user.controller.d.ts +2 -2
- package/dist/controllers/user.controller.js +2 -2
- package/dist/controllers/view-metadata.controller.d.ts +2 -2
- package/dist/controllers/view-metadata.controller.js +1 -1
- package/dist/controllers/workflow-definition.controller.d.ts +1 -1
- package/dist/controllers/workflow-definition.controller.js +1 -1
- package/dist/controllers/workflow-execution-artifact.controller.d.ts +1 -1
- package/dist/controllers/workflow-execution-artifact.controller.js +1 -1
- package/dist/controllers/workflow-execution-log.controller.d.ts +1 -1
- package/dist/controllers/workflow-execution-log.controller.js +1 -1
- package/dist/controllers/workflow-execution.controller.d.ts +1 -1
- package/dist/controllers/workflow-execution.controller.js +1 -1
- package/dist/controllers/workflow-secret.controller.d.ts +1 -1
- package/dist/controllers/workflow-secret.controller.js +1 -1
- package/dist/controllers/workflow-step-execution.controller.d.ts +1 -1
- package/dist/controllers/workflow-step-execution.controller.js +1 -1
- package/dist/controllers/workflow-trigger-execution.controller.d.ts +1 -1
- package/dist/controllers/workflow-trigger-execution.controller.js +1 -1
- package/dist/dtos/basic-filters.dto.d.ts +0 -1
- package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.js +1 -6
- package/dist/dtos/basic-filters.dto.js.map +1 -1
- package/dist/entities/common.entity.d.ts +4 -0
- package/dist/entities/common.entity.d.ts.map +1 -1
- package/dist/entities/common.entity.js +25 -1
- package/dist/entities/common.entity.js.map +1 -1
- package/dist/entities/user.entity.js +2 -2
- package/dist/entities/user.entity.js.map +1 -1
- package/dist/factories/mail.factory.d.ts.map +1 -1
- package/dist/factories/mail.factory.js +4 -0
- package/dist/factories/mail.factory.js.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +1 -0
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +18 -11
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/helpers/file-download.helper.d.ts +9 -0
- package/dist/helpers/file-download.helper.d.ts.map +1 -0
- package/dist/helpers/file-download.helper.js +14 -0
- package/dist/helpers/file-download.helper.js.map +1 -0
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +30 -1
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/solid-registry.js +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/seeders/module-test-data.service.d.ts +3 -1
- package/dist/seeders/module-test-data.service.d.ts.map +1 -1
- package/dist/seeders/module-test-data.service.js +8 -2
- package/dist/seeders/module-test-data.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +2 -0
- package/dist/services/authentication.service.d.ts +4 -1
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +52 -4
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts +1 -0
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +8 -0
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts +5 -2
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +30 -32
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +11 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +79 -66
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/draft-publish-helper.service.d.ts +52 -0
- package/dist/services/draft-publish-helper.service.d.ts.map +1 -0
- package/dist/services/draft-publish-helper.service.js +368 -0
- package/dist/services/draft-publish-helper.service.js.map +1 -0
- package/dist/services/internationalisation-helper.service.d.ts +19 -0
- package/dist/services/internationalisation-helper.service.d.ts.map +1 -0
- package/dist/services/internationalisation-helper.service.js +107 -0
- package/dist/services/internationalisation-helper.service.js.map +1 -0
- package/dist/services/locale.service.d.ts +1 -1
- package/dist/services/media.service.d.ts +2 -1
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +28 -2
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/metadata-validation.service.d.ts +7 -0
- package/dist/services/metadata-validation.service.d.ts.map +1 -0
- package/dist/services/metadata-validation.service.js +42 -0
- package/dist/services/metadata-validation.service.js.map +1 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +2 -1
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/object.utils.d.ts +2 -0
- package/dist/services/object.utils.d.ts.map +1 -0
- package/dist/services/object.utils.js +11 -0
- package/dist/services/object.utils.js.map +1 -0
- package/dist/services/settings/default-settings-provider.service.d.ts +6 -6
- package/dist/services/settings/default-settings-provider.service.js +3 -3
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/user.service.d.ts +2 -2
- package/dist/services/user.service.d.ts.map +1 -1
- package/dist/services/user.service.js +9 -3
- package/dist/services/user.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts +1 -2
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +3 -55
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/services/workflow-definition.service.d.ts +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +10 -1
- package/dist/solid-core.module.js.map +1 -1
- package/package.json +1 -1
- package/src/constants/draft-publish-fields.ts +5 -0
- package/src/constants/media-file-types.ts +50 -0
- package/src/controllers/export-template.controller.ts +5 -3
- package/src/controllers/import-transaction.controller.ts +6 -6
- package/src/controllers/media.controller.ts +17 -6
- package/src/controllers/module-package.controller.ts +2 -3
- package/src/dtos/basic-filters.dto.ts +0 -4
- package/src/entities/common.entity.ts +20 -0
- package/src/entities/user.entity.ts +2 -2
- package/src/factories/mail.factory.ts +7 -1
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +26 -12
- package/src/helpers/file-download.helper.ts +31 -0
- package/src/helpers/model-metadata-helper.service.ts +32 -1
- package/src/helpers/solid-registry.ts +1 -1
- package/src/index.ts +2 -0
- package/src/seeders/module-test-data.service.ts +6 -0
- package/src/seeders/seed-data/solid-core-metadata.json +2 -0
- package/src/services/1.js +6 -0
- package/src/services/authentication.service.ts +76 -5
- package/src/services/chatter-message.service.ts +16 -0
- package/src/services/crud-helper.service.ts +16 -34
- package/src/services/crud.service.ts +98 -103
- package/src/services/datasource-introspection.service.ts +5 -0
- package/src/services/draft-publish-helper.service.ts +529 -0
- package/src/services/internationalisation-helper.service.ts +148 -0
- package/src/services/media.service.ts +48 -3
- package/src/services/metadata-validation.service.ts +39 -0
- package/src/services/model-metadata.service.ts +4 -1
- package/src/services/object.utils.ts +10 -0
- package/src/services/settings/default-settings-provider.service.ts +3 -3
- package/src/services/user.service.ts +18 -3
- package/src/services/view-metadata.service.ts +5 -145
- package/src/solid-core.module.ts +16 -3
- package/dist-tests/api/authenticate.spec.js +0 -119
- package/dist-tests/api/authenticate.spec.js.map +0 -1
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
- package/dist-tests/api/ping.spec.js +0 -21
- package/dist-tests/api/ping.spec.js.map +0 -1
- package/dist-tests/helpers/auth.js +0 -41
- package/dist-tests/helpers/auth.js.map +0 -1
- package/dist-tests/helpers/env.js +0 -11
- package/dist-tests/helpers/env.js.map +0 -1
- package/docs/agent-builder/notebook-lm-prompt-for-agent-builder.md +0 -136
- package/docs/agent-builder/team-ready-note-agent-builder.md +0 -305
- package/docs/agent-hub-grooming.md +0 -301
- package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
- package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
- package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
- package/docs/datasource-introspection-ddl-analysis.md +0 -326
- package/docs/datasource-introspection-implementation-plan.md +0 -306
- package/docs/grouping-enhancements.md +0 -89
- package/docs/java-spring/README.md +0 -3
- package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
- package/docs/module-package-import-handoff.md +0 -691
- package/docs/seed-changes.md +0 -65
- package/docs/test-data-workflow.md +0 -200
- package/docs/type-declaration-import-issue.md +0 -24
- package/docs/workflow/business-automation-example-notes.md +0 -309
- package/docs/workflow/control-flow-node-addition-sop.md +0 -324
- package/docs/workflow/data-engineering-pipeline-example-notes.md +0 -330
- package/docs/workflow/foreach-example-notes.md +0 -187
- package/docs/workflow/hello-world-example-notes.md +0 -271
- package/docs/workflow/kestra-concepts-plugins-blueprints.md +0 -315
- package/docs/workflow/loop-until-example-notes.md +0 -198
- package/docs/workflow/microservices-and-apis-example-notes.md +0 -264
- package/docs/workflow/samples.md +0 -25
- package/docs/workflow/what-is-kestra.md +0 -79
- package/docs/workflow/workflow-core-module-handoff-summary.md +0 -191
- package/docs/workflow/workflow-module-brd.md +0 -185
- package/docs/workflow/workflow-module-domain-model.md +0 -252
- package/docs/workflow/workflow-module-metadata-dsl.md +0 -302
- package/docs/workflow/workflow-module-technical-spec.md +0 -293
- package/docs/workflow/workflow-node-type-contracts.md +0 -635
- package/docs/workflow/workflow-node-ui-metadata-contract.md +0 -1262
- package/docs/workflow/workflow-solidx-model-field-spec.md +0 -284
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { EntityManager, SelectQueryBuilder } from "typeorm";
|
|
2
|
+
import { CommonEntity } from "../entities/common.entity";
|
|
3
|
+
import { FieldMetadata } from "../entities/field-metadata.entity";
|
|
4
|
+
import { ModelMetadata } from "../entities/model-metadata.entity";
|
|
5
|
+
import { SolidBaseRepository } from "../repository/solid-base.repository";
|
|
6
|
+
type TransformDtoResult = {
|
|
7
|
+
dto: any;
|
|
8
|
+
hasMediaFields: boolean;
|
|
9
|
+
};
|
|
10
|
+
type DraftPublishCrudContext<T extends CommonEntity> = {
|
|
11
|
+
repo: SolidBaseRepository<T>;
|
|
12
|
+
modelName: string;
|
|
13
|
+
moduleName: string;
|
|
14
|
+
validateAndTransformDto: (field: FieldMetadata, dto: any, files: Express.Multer.File[], hasMediaFields: boolean, isPartialUpdate?: boolean, isUpdate?: boolean, entityId?: number) => Promise<TransformDtoResult>;
|
|
15
|
+
saveMedia: (model: ModelMetadata, files: Express.Multer.File[], savedEntity: T) => Promise<void>;
|
|
16
|
+
getDatasourceDefaultEntityManager: () => EntityManager;
|
|
17
|
+
getColumnDatabaseName: (propertyName: string) => string;
|
|
18
|
+
escapeDatabaseName: (databaseName: string) => string;
|
|
19
|
+
};
|
|
20
|
+
export declare class DraftPublishHelperService {
|
|
21
|
+
isDraftPublishEnabled(model: ModelMetadata): boolean;
|
|
22
|
+
assertDraftPublishWorkflowEnabled(model: ModelMetadata, modelName: string): void;
|
|
23
|
+
assertUpdateAllowed<T extends CommonEntity>(model: ModelMetadata, entity: T, modelName: string): void;
|
|
24
|
+
shouldCopyPublishedVersionForUpdate<T extends CommonEntity>(model: ModelMetadata, entity: T): boolean;
|
|
25
|
+
applyCreateDefaults(model: ModelMetadata, createDto: any): void;
|
|
26
|
+
ensureInitialEntityVersionId<T extends CommonEntity>(model: ModelMetadata, repo: SolidBaseRepository<T>, savedEntity: T): Promise<T>;
|
|
27
|
+
applyDraftPublishFilterDefaults(qb: SelectQueryBuilder<any>, filters: any, entityAlias: string): void;
|
|
28
|
+
filtersContainField(filters: any, fieldName: string): boolean;
|
|
29
|
+
copyPublishedVersionAndUpdate<T extends CommonEntity>(context: DraftPublishCrudContext<T>, id: number, updateDto: any, files: Express.Multer.File[], isPartialUpdate: boolean, model: ModelMetadata): Promise<T>;
|
|
30
|
+
assertDraftPublishDeleteAllowed<T extends CommonEntity>(modelName: string, entities: T[]): void;
|
|
31
|
+
markDeletedDraftPublishVersionsAsNotLatest<T extends CommonEntity>(model: ModelMetadata, entities: T[]): void;
|
|
32
|
+
getLatestDraftPublishChainIds<T extends CommonEntity>(entities: T[]): number[];
|
|
33
|
+
getEntityIds<T extends CommonEntity>(entities: T[]): number[];
|
|
34
|
+
promoteLatestDraftPublishVersionAfterDelete<T extends CommonEntity>(repo: SolidBaseRepository<T>, deletedLatestChainIds: number[], deletedIds: number[]): Promise<void>;
|
|
35
|
+
private promoteReplacementForChain;
|
|
36
|
+
promoteRecoveredDraftPublishVersion<T extends CommonEntity>(repo: SolidBaseRepository<T>, recoveredEntity: T): Promise<void>;
|
|
37
|
+
publishRecord<T extends CommonEntity>(repo: SolidBaseRepository<T>, model: ModelMetadata, modelName: string, id: number, activeUser?: any): Promise<T>;
|
|
38
|
+
unpublishRecord<T extends CommonEntity>(repo: SolidBaseRepository<T>, model: ModelMetadata, modelName: string, id: number, activeUser?: any): Promise<T>;
|
|
39
|
+
private loadLatestEntityForPublishAction;
|
|
40
|
+
private someFilterLeaf;
|
|
41
|
+
private normalizeBooleanFilterValue;
|
|
42
|
+
private filtersContainBooleanValue;
|
|
43
|
+
private extractBooleanFilterValues;
|
|
44
|
+
private getVersionedRelationNames;
|
|
45
|
+
private wasFieldSubmitted;
|
|
46
|
+
private buildNewVersionPayload;
|
|
47
|
+
private cloneMediaForVersion;
|
|
48
|
+
private createPublishedVersionTracker;
|
|
49
|
+
private createTemporaryPublishedVersionTracker;
|
|
50
|
+
}
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=draft-publish-helper.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-publish-helper.service.d.ts","sourceRoot":"","sources":["../../src/services/draft-publish-helper.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DraftPublishHelperService = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const typeorm_1 = require("typeorm");
|
|
6
|
+
const draft_publish_fields_1 = require("../constants/draft-publish-fields");
|
|
7
|
+
const create_field_metadata_dto_1 = require("../dtos/create-field-metadata.dto");
|
|
8
|
+
const media_entity_1 = require("../entities/media.entity");
|
|
9
|
+
const object_utils_1 = require("./object.utils");
|
|
10
|
+
class DraftPublishHelperService {
|
|
11
|
+
isDraftPublishEnabled(model) {
|
|
12
|
+
return model?.draftPublishWorkflow === true;
|
|
13
|
+
}
|
|
14
|
+
assertDraftPublishWorkflowEnabled(model, modelName) {
|
|
15
|
+
if (!this.isDraftPublishEnabled(model)) {
|
|
16
|
+
throw new common_1.BadRequestException(`Publish workflow is not enabled for ${modelName}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
assertUpdateAllowed(model, entity, modelName) {
|
|
20
|
+
if (!this.isDraftPublishEnabled(model))
|
|
21
|
+
return;
|
|
22
|
+
if (entity.isLatest === false) {
|
|
23
|
+
throw new common_1.BadRequestException(`Only the latest version of ${modelName} can be updated.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
shouldCopyPublishedVersionForUpdate(model, entity) {
|
|
27
|
+
return this.isDraftPublishEnabled(model) && entity.isPublished === true;
|
|
28
|
+
}
|
|
29
|
+
applyCreateDefaults(model, createDto) {
|
|
30
|
+
if (!this.isDraftPublishEnabled(model))
|
|
31
|
+
return;
|
|
32
|
+
createDto.isLatest = true;
|
|
33
|
+
createDto.isPublished = false;
|
|
34
|
+
createDto.publishedAt = null;
|
|
35
|
+
createDto.publishedTracker = "na";
|
|
36
|
+
}
|
|
37
|
+
async ensureInitialEntityVersionId(model, repo, savedEntity) {
|
|
38
|
+
if (!this.isDraftPublishEnabled(model) || savedEntity.initialEntityVersionId)
|
|
39
|
+
return savedEntity;
|
|
40
|
+
savedEntity.initialEntityVersionId = savedEntity.id;
|
|
41
|
+
return repo.save(savedEntity);
|
|
42
|
+
}
|
|
43
|
+
applyDraftPublishFilterDefaults(qb, filters, entityAlias) {
|
|
44
|
+
const hasIsLatestFilter = this.filtersContainField(filters, 'isLatest');
|
|
45
|
+
const hasInitialVersionFilter = this.filtersContainField(filters, 'initialEntityVersionId');
|
|
46
|
+
const isPublishedViewRequested = this.filtersContainBooleanValue(filters, 'isPublished', true);
|
|
47
|
+
const shouldApplyDefaultLatestFilter = !isPublishedViewRequested && !hasIsLatestFilter && !hasInitialVersionFilter;
|
|
48
|
+
if (shouldApplyDefaultLatestFilter) {
|
|
49
|
+
qb.andWhere(`(${entityAlias}.isLatest = :defaultIsLatest OR ${entityAlias}.deletedAt IS NOT NULL)`, { defaultIsLatest: true });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
filtersContainField(filters, fieldName) {
|
|
53
|
+
return this.someFilterLeaf(filters, fieldName, () => true);
|
|
54
|
+
}
|
|
55
|
+
async copyPublishedVersionAndUpdate(context, id, updateDto, files, isPartialUpdate, model) {
|
|
56
|
+
const relationNames = this.getVersionedRelationNames(model);
|
|
57
|
+
const sourceEntity = await context.repo.findOne({
|
|
58
|
+
where: { id },
|
|
59
|
+
relations: relationNames,
|
|
60
|
+
});
|
|
61
|
+
if (!sourceEntity) {
|
|
62
|
+
throw new Error(`Entity [${context.moduleName}.${context.modelName}] with id ${id} not found`);
|
|
63
|
+
}
|
|
64
|
+
let hasMediaFields = false;
|
|
65
|
+
const submittedDto = updateDto;
|
|
66
|
+
let transformedDto = { ...updateDto };
|
|
67
|
+
for (const field of model.fields) {
|
|
68
|
+
const transformed = await context.validateAndTransformDto(field, transformedDto, files, hasMediaFields, isPartialUpdate, false);
|
|
69
|
+
transformedDto = transformed.dto;
|
|
70
|
+
hasMediaFields = transformed.hasMediaFields;
|
|
71
|
+
}
|
|
72
|
+
const newVersionPayload = this.buildNewVersionPayload(context.repo, sourceEntity, relationNames);
|
|
73
|
+
for (const field of model.fields) {
|
|
74
|
+
if (this.wasFieldSubmitted(submittedDto, field) && Object.prototype.hasOwnProperty.call(transformedDto, field.name)) {
|
|
75
|
+
newVersionPayload[field.name] = transformedDto[field.name];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const chainId = sourceEntity.initialEntityVersionId || sourceEntity.id;
|
|
79
|
+
newVersionPayload.initialEntityVersionId = chainId;
|
|
80
|
+
newVersionPayload.isLatest = true;
|
|
81
|
+
newVersionPayload.isPublished = false;
|
|
82
|
+
newVersionPayload.publishedAt = null;
|
|
83
|
+
newVersionPayload.publishedTracker = this.createTemporaryPublishedVersionTracker();
|
|
84
|
+
const savedVersion = await context.repo.manager.transaction(async (manager) => {
|
|
85
|
+
const transactionalRepo = manager.getRepository(context.repo.metadata.target);
|
|
86
|
+
await manager
|
|
87
|
+
.createQueryBuilder()
|
|
88
|
+
.update(context.repo.metadata.target)
|
|
89
|
+
.set({ isLatest: false })
|
|
90
|
+
.where(`${context.escapeDatabaseName(context.getColumnDatabaseName('isLatest'))} = :isLatest`, { isLatest: true })
|
|
91
|
+
.andWhere(`(${context.escapeDatabaseName(context.getColumnDatabaseName('initialEntityVersionId'))} = :chainId OR ${context.escapeDatabaseName(context.getColumnDatabaseName('id'))} = :chainId)`, { chainId })
|
|
92
|
+
.callListeners(false)
|
|
93
|
+
.execute();
|
|
94
|
+
const newVersion = transactionalRepo.create(newVersionPayload);
|
|
95
|
+
let persistedVersion = await transactionalRepo.save(newVersion);
|
|
96
|
+
persistedVersion.publishedTracker = this.createPublishedVersionTracker(persistedVersion.id);
|
|
97
|
+
persistedVersion = await transactionalRepo.save(persistedVersion);
|
|
98
|
+
await this.cloneMediaForVersion(context, model, sourceEntity.id, persistedVersion, files, manager);
|
|
99
|
+
return persistedVersion;
|
|
100
|
+
});
|
|
101
|
+
if (hasMediaFields) {
|
|
102
|
+
await context.saveMedia(model, files, savedVersion);
|
|
103
|
+
}
|
|
104
|
+
return savedVersion;
|
|
105
|
+
}
|
|
106
|
+
assertDraftPublishDeleteAllowed(modelName, entities) {
|
|
107
|
+
const currentPublishedEntityIds = entities
|
|
108
|
+
.filter(entity => entity.isPublished === true)
|
|
109
|
+
.map(entity => entity.id);
|
|
110
|
+
if (currentPublishedEntityIds.length > 0) {
|
|
111
|
+
throw new common_1.BadRequestException(`Published ${modelName} record cannot be deleted. Publish another draft or unpublish it before deleting. Invalid Ids ${currentPublishedEntityIds.join(', ')}.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
markDeletedDraftPublishVersionsAsNotLatest(model, entities) {
|
|
115
|
+
if (!this.isDraftPublishEnabled(model))
|
|
116
|
+
return;
|
|
117
|
+
for (const entity of entities) {
|
|
118
|
+
if (entity.isLatest === true) {
|
|
119
|
+
entity.isLatest = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
getLatestDraftPublishChainIds(entities) {
|
|
124
|
+
return Array.from(new Set(entities
|
|
125
|
+
.filter(entity => entity.isLatest === true)
|
|
126
|
+
.map(entity => entity.initialEntityVersionId || entity.id)));
|
|
127
|
+
}
|
|
128
|
+
getEntityIds(entities) {
|
|
129
|
+
return entities.map(entity => entity.id).filter(Boolean);
|
|
130
|
+
}
|
|
131
|
+
async promoteLatestDraftPublishVersionAfterDelete(repo, deletedLatestChainIds, deletedIds) {
|
|
132
|
+
if (deletedLatestChainIds.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
await Promise.all(deletedLatestChainIds.map(chainId => this.promoteReplacementForChain(repo, chainId, deletedIds)));
|
|
135
|
+
}
|
|
136
|
+
async promoteReplacementForChain(repo, chainId, deletedIds) {
|
|
137
|
+
const replacementQuery = repo.manager
|
|
138
|
+
.createQueryBuilder(repo.metadata.target, 'entity')
|
|
139
|
+
.where('(entity.initialEntityVersionId = :chainId OR entity.id = :chainId)', { chainId })
|
|
140
|
+
.orderBy('entity.createdAt', 'DESC')
|
|
141
|
+
.addOrderBy('entity.id', 'DESC');
|
|
142
|
+
if (deletedIds.length > 0) {
|
|
143
|
+
replacementQuery.andWhere('entity.id NOT IN (:...deletedIds)', { deletedIds });
|
|
144
|
+
}
|
|
145
|
+
const replacement = await replacementQuery.getOne();
|
|
146
|
+
if (!replacement)
|
|
147
|
+
return;
|
|
148
|
+
await repo.manager
|
|
149
|
+
.createQueryBuilder()
|
|
150
|
+
.update(repo.metadata.target)
|
|
151
|
+
.set({ isLatest: false })
|
|
152
|
+
.where('(initial_entity_version_id = :chainId OR id = :chainId)', { chainId })
|
|
153
|
+
.execute();
|
|
154
|
+
await repo.update(replacement.id, { isLatest: true });
|
|
155
|
+
}
|
|
156
|
+
async promoteRecoveredDraftPublishVersion(repo, recoveredEntity) {
|
|
157
|
+
const chainId = recoveredEntity.initialEntityVersionId || recoveredEntity.id;
|
|
158
|
+
await repo.manager
|
|
159
|
+
.createQueryBuilder()
|
|
160
|
+
.update(repo.metadata.target)
|
|
161
|
+
.set({ isLatest: false })
|
|
162
|
+
.where('(initial_entity_version_id = :chainId OR id = :chainId)', { chainId })
|
|
163
|
+
.execute();
|
|
164
|
+
await repo.update(recoveredEntity.id, { isLatest: true });
|
|
165
|
+
recoveredEntity.isLatest = true;
|
|
166
|
+
}
|
|
167
|
+
async publishRecord(repo, model, modelName, id, activeUser) {
|
|
168
|
+
const entity = await this.loadLatestEntityForPublishAction(repo, model, modelName, id, 'published');
|
|
169
|
+
if (entity.isPublished) {
|
|
170
|
+
throw new common_1.BadRequestException(`${modelName} with id ${id} is already published`);
|
|
171
|
+
}
|
|
172
|
+
const chainId = entity.initialEntityVersionId || entity.id;
|
|
173
|
+
return repo.manager.transaction(async (manager) => {
|
|
174
|
+
const transactionalRepo = manager.getRepository(repo.metadata.target);
|
|
175
|
+
const chainVersions = await transactionalRepo.find({
|
|
176
|
+
where: [
|
|
177
|
+
{ initialEntityVersionId: chainId },
|
|
178
|
+
{ id: chainId },
|
|
179
|
+
],
|
|
180
|
+
});
|
|
181
|
+
const archivedVersions = chainVersions
|
|
182
|
+
.filter(version => version.id !== entity.id)
|
|
183
|
+
.map(version => ({
|
|
184
|
+
...version,
|
|
185
|
+
isPublished: false,
|
|
186
|
+
publishedTracker: this.createPublishedVersionTracker(version.id),
|
|
187
|
+
}));
|
|
188
|
+
if (archivedVersions.length > 0) {
|
|
189
|
+
await transactionalRepo.save(archivedVersions);
|
|
190
|
+
}
|
|
191
|
+
return transactionalRepo.save({
|
|
192
|
+
...entity,
|
|
193
|
+
initialEntityVersionId: chainId,
|
|
194
|
+
isPublished: true,
|
|
195
|
+
publishedAt: new Date(),
|
|
196
|
+
publishedTracker: "na",
|
|
197
|
+
updatedBy: activeUser?.sub ?? entity.updatedBy,
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async unpublishRecord(repo, model, modelName, id, activeUser) {
|
|
202
|
+
const entity = await this.loadLatestEntityForPublishAction(repo, model, modelName, id, 'unpublished');
|
|
203
|
+
if (!entity.isPublished) {
|
|
204
|
+
throw new common_1.BadRequestException(`${modelName} with id ${id} is already unpublished`);
|
|
205
|
+
}
|
|
206
|
+
return repo.save({
|
|
207
|
+
...entity,
|
|
208
|
+
isPublished: false,
|
|
209
|
+
updatedBy: activeUser?.sub ?? entity.updatedBy,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async loadLatestEntityForPublishAction(repo, model, modelName, id, action) {
|
|
213
|
+
this.assertDraftPublishWorkflowEnabled(model, modelName);
|
|
214
|
+
const entity = await repo.findOne({ where: { id } });
|
|
215
|
+
if (!entity) {
|
|
216
|
+
throw new common_1.NotFoundException(`${modelName} with id ${id} not found`);
|
|
217
|
+
}
|
|
218
|
+
if (entity.isLatest === false) {
|
|
219
|
+
throw new common_1.BadRequestException(`Only the latest version of ${modelName} can be ${action}.`);
|
|
220
|
+
}
|
|
221
|
+
return entity;
|
|
222
|
+
}
|
|
223
|
+
someFilterLeaf(filters, fieldName, matchesLeaf) {
|
|
224
|
+
if (!filters || typeof filters !== 'object')
|
|
225
|
+
return false;
|
|
226
|
+
const normalizedFilters = (0, object_utils_1.normalizeObjectKeys)(filters);
|
|
227
|
+
return Object.keys(normalizedFilters).some(key => {
|
|
228
|
+
const [rawField] = key.split(':');
|
|
229
|
+
const value = normalizedFilters[key];
|
|
230
|
+
if (rawField === fieldName)
|
|
231
|
+
return matchesLeaf(value);
|
|
232
|
+
if (key === '$and' || key === '$or') {
|
|
233
|
+
return Array.isArray(value) && value.some((nestedFilter) => this.someFilterLeaf(nestedFilter, fieldName, matchesLeaf));
|
|
234
|
+
}
|
|
235
|
+
return value && typeof value === 'object' && this.someFilterLeaf(value, fieldName, matchesLeaf);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
normalizeBooleanFilterValue(value) {
|
|
239
|
+
if (value === undefined || value === null || value === '')
|
|
240
|
+
return undefined;
|
|
241
|
+
if (typeof value === 'boolean')
|
|
242
|
+
return value;
|
|
243
|
+
if (typeof value === 'string') {
|
|
244
|
+
const normalized = value.toLowerCase();
|
|
245
|
+
if (normalized === 'true')
|
|
246
|
+
return true;
|
|
247
|
+
if (normalized === 'false')
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
filtersContainBooleanValue(filters, fieldName, expectedValue) {
|
|
253
|
+
return this.someFilterLeaf(filters, fieldName, value => this.extractBooleanFilterValues(value).includes(expectedValue));
|
|
254
|
+
}
|
|
255
|
+
extractBooleanFilterValues(filterValue) {
|
|
256
|
+
if (filterValue === undefined || filterValue === null)
|
|
257
|
+
return [];
|
|
258
|
+
const directValue = this.normalizeBooleanFilterValue(filterValue);
|
|
259
|
+
if (directValue !== undefined)
|
|
260
|
+
return [directValue];
|
|
261
|
+
if (Array.isArray(filterValue)) {
|
|
262
|
+
return filterValue
|
|
263
|
+
.map(value => this.normalizeBooleanFilterValue(value))
|
|
264
|
+
.filter((value) => value !== undefined);
|
|
265
|
+
}
|
|
266
|
+
if (typeof filterValue !== 'object')
|
|
267
|
+
return [];
|
|
268
|
+
const normalizedValue = (0, object_utils_1.normalizeObjectKeys)(filterValue);
|
|
269
|
+
return Object.keys(normalizedValue)
|
|
270
|
+
.filter(operator => operator === '$eq' || operator === '$in')
|
|
271
|
+
.flatMap(operator => this.extractBooleanFilterValues(normalizedValue[operator]));
|
|
272
|
+
}
|
|
273
|
+
getVersionedRelationNames(model) {
|
|
274
|
+
return model.fields
|
|
275
|
+
.filter(field => field.type === create_field_metadata_dto_1.SolidFieldType.relation && field.relationType !== create_field_metadata_dto_1.RelationType.oneToMany)
|
|
276
|
+
.map(field => field.name);
|
|
277
|
+
}
|
|
278
|
+
wasFieldSubmitted(dto, field) {
|
|
279
|
+
if (!dto)
|
|
280
|
+
return false;
|
|
281
|
+
if (Object.prototype.hasOwnProperty.call(dto, field.name))
|
|
282
|
+
return true;
|
|
283
|
+
if (field.type !== create_field_metadata_dto_1.SolidFieldType.relation)
|
|
284
|
+
return false;
|
|
285
|
+
if (Object.prototype.hasOwnProperty.call(dto, `${field.name}Id`))
|
|
286
|
+
return true;
|
|
287
|
+
if (Object.prototype.hasOwnProperty.call(dto, `${field.name}Ids`))
|
|
288
|
+
return true;
|
|
289
|
+
if (Object.prototype.hasOwnProperty.call(dto, `${field.name}UserKey`))
|
|
290
|
+
return true;
|
|
291
|
+
if (Object.prototype.hasOwnProperty.call(dto, `${field.name}Command`))
|
|
292
|
+
return true;
|
|
293
|
+
if (field.relationCoModelFieldName) {
|
|
294
|
+
if (Object.prototype.hasOwnProperty.call(dto, `${field.relationCoModelFieldName}Ids`))
|
|
295
|
+
return true;
|
|
296
|
+
if (Object.prototype.hasOwnProperty.call(dto, `${field.relationCoModelFieldName}Command`))
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
buildNewVersionPayload(repo, sourceEntity, relationNames) {
|
|
302
|
+
const newVersionPayload = {};
|
|
303
|
+
const excludedColumns = new Set([
|
|
304
|
+
'id',
|
|
305
|
+
'createdAt',
|
|
306
|
+
'updatedAt',
|
|
307
|
+
'deletedAt',
|
|
308
|
+
'createdBy',
|
|
309
|
+
'updatedBy',
|
|
310
|
+
'publishedAt',
|
|
311
|
+
...draft_publish_fields_1.DRAFT_PUBLISH_VERSIONING_FIELD_NAMES,
|
|
312
|
+
]);
|
|
313
|
+
repo.metadata.columns.forEach(column => {
|
|
314
|
+
if (!excludedColumns.has(column.propertyName)) {
|
|
315
|
+
newVersionPayload[column.propertyName] = sourceEntity[column.propertyName];
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
for (const relationName of relationNames) {
|
|
319
|
+
newVersionPayload[relationName] = sourceEntity[relationName];
|
|
320
|
+
}
|
|
321
|
+
return newVersionPayload;
|
|
322
|
+
}
|
|
323
|
+
async cloneMediaForVersion(context, model, sourceEntityId, targetEntity, files = [], entityManager) {
|
|
324
|
+
const mediaFields = model.fields.filter(field => field.type === create_field_metadata_dto_1.SolidFieldType.mediaSingle || field.type === create_field_metadata_dto_1.SolidFieldType.mediaMultiple);
|
|
325
|
+
if (mediaFields.length === 0)
|
|
326
|
+
return;
|
|
327
|
+
const uploadedFieldNames = new Set(files.map(file => file.fieldname));
|
|
328
|
+
const clonableFieldIds = mediaFields
|
|
329
|
+
.filter(field => !uploadedFieldNames.has(field.name))
|
|
330
|
+
.map(field => field.id);
|
|
331
|
+
if (clonableFieldIds.length === 0)
|
|
332
|
+
return;
|
|
333
|
+
const mediaRepository = (entityManager ?? context.getDatasourceDefaultEntityManager()).getRepository(media_entity_1.Media);
|
|
334
|
+
const sourceMedia = await mediaRepository.find({
|
|
335
|
+
where: {
|
|
336
|
+
entityId: sourceEntityId,
|
|
337
|
+
modelMetadata: { id: model.id },
|
|
338
|
+
fieldMetadata: { id: (0, typeorm_1.In)(clonableFieldIds) },
|
|
339
|
+
},
|
|
340
|
+
relations: {
|
|
341
|
+
modelMetadata: true,
|
|
342
|
+
mediaStorageProviderMetadata: true,
|
|
343
|
+
fieldMetadata: true,
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
if (sourceMedia.length === 0)
|
|
347
|
+
return;
|
|
348
|
+
const clonedMedia = sourceMedia.map(media => mediaRepository.create({
|
|
349
|
+
entityId: targetEntity.id,
|
|
350
|
+
relativeUri: media.relativeUri,
|
|
351
|
+
fileSize: media.fileSize,
|
|
352
|
+
mimeType: media.mimeType,
|
|
353
|
+
originalFileName: media.originalFileName,
|
|
354
|
+
modelMetadata: media.modelMetadata,
|
|
355
|
+
mediaStorageProviderMetadata: media.mediaStorageProviderMetadata,
|
|
356
|
+
fieldMetadata: media.fieldMetadata,
|
|
357
|
+
}));
|
|
358
|
+
await mediaRepository.save(clonedMedia);
|
|
359
|
+
}
|
|
360
|
+
createPublishedVersionTracker(seed) {
|
|
361
|
+
return `version:${seed}`;
|
|
362
|
+
}
|
|
363
|
+
createTemporaryPublishedVersionTracker() {
|
|
364
|
+
return `version:pending:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
exports.DraftPublishHelperService = DraftPublishHelperService;
|
|
368
|
+
//# sourceMappingURL=draft-publish-helper.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-publish-helper.service.js","sourceRoot":"","sources":["../../src/services/draft-publish-helper.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { BadRequestException, NotFoundException } from \"@nestjs/common\";\nimport { EntityManager, FindOptionsWhere, In, SelectQueryBuilder } from \"typeorm\";\nimport { QueryDeepPartialEntity } from \"typeorm/query-builder/QueryPartialEntity\";\nimport { DRAFT_PUBLISH_VERSIONING_FIELD_NAMES } from \"../constants/draft-publish-fields\";\nimport { RelationType, SolidFieldType } from \"../dtos/create-field-metadata.dto\";\nimport { CommonEntity } from \"../entities/common.entity\";\nimport { FieldMetadata } from \"../entities/field-metadata.entity\";\nimport { Media } from \"../entities/media.entity\";\nimport { ModelMetadata } from \"../entities/model-metadata.entity\";\nimport { SolidBaseRepository } from \"../repository/solid-base.repository\";\nimport { normalizeObjectKeys } from \"./object.utils\";\n\ntype TransformDtoResult = {\n dto: any;\n hasMediaFields: boolean;\n};\n\ntype DraftPublishCrudContext<T extends CommonEntity> = {\n repo: SolidBaseRepository<T>;\n modelName: string;\n moduleName: string;\n validateAndTransformDto: (\n field: FieldMetadata,\n dto: any,\n files: Express.Multer.File[],\n hasMediaFields: boolean,\n isPartialUpdate?: boolean,\n isUpdate?: boolean,\n entityId?: number,\n ) => Promise<TransformDtoResult>;\n saveMedia: (model: ModelMetadata, files: Express.Multer.File[], savedEntity: T) => Promise<void>;\n getDatasourceDefaultEntityManager: () => EntityManager;\n getColumnDatabaseName: (propertyName: string) => string;\n escapeDatabaseName: (databaseName: string) => string;\n};\n\nexport class DraftPublishHelperService {\n isDraftPublishEnabled(model: ModelMetadata): boolean {\n return model?.draftPublishWorkflow === true;\n }\n\n assertDraftPublishWorkflowEnabled(model: ModelMetadata, modelName: string): void {\n if (!this.isDraftPublishEnabled(model)) {\n throw new BadRequestException(`Publish workflow is not enabled for ${modelName}`);\n }\n }\n\n assertUpdateAllowed<T extends CommonEntity>(model: ModelMetadata, entity: T, modelName: string): void {\n if (!this.isDraftPublishEnabled(model)) return;\n if (entity.isLatest === false) {\n throw new BadRequestException(`Only the latest version of ${modelName} can be updated.`);\n }\n }\n\n shouldCopyPublishedVersionForUpdate<T extends CommonEntity>(model: ModelMetadata, entity: T): boolean {\n return this.isDraftPublishEnabled(model) && entity.isPublished === true;\n }\n\n applyCreateDefaults(model: ModelMetadata, createDto: any): void {\n if (!this.isDraftPublishEnabled(model)) return;\n createDto.isLatest = true;\n createDto.isPublished = false;\n createDto.publishedAt = null;\n createDto.publishedTracker = \"na\";\n }\n\n async ensureInitialEntityVersionId<T extends CommonEntity>(\n model: ModelMetadata,\n repo: SolidBaseRepository<T>,\n savedEntity: T,\n ): Promise<T> {\n if (!this.isDraftPublishEnabled(model) || savedEntity.initialEntityVersionId) return savedEntity;\n savedEntity.initialEntityVersionId = savedEntity.id;\n return repo.save(savedEntity) as Promise<T>;\n }\n\n applyDraftPublishFilterDefaults(\n qb: SelectQueryBuilder<any>,\n filters: any,\n entityAlias: string,\n ): void {\n const hasIsLatestFilter = this.filtersContainField(filters, 'isLatest');\n const hasInitialVersionFilter = this.filtersContainField(filters, 'initialEntityVersionId');\n const isPublishedViewRequested = this.filtersContainBooleanValue(filters, 'isPublished', true);\n const shouldApplyDefaultLatestFilter = !isPublishedViewRequested && !hasIsLatestFilter && !hasInitialVersionFilter;\n\n if (shouldApplyDefaultLatestFilter) {\n // OR in deletedAt IS NOT NULL so soft-deleted rows — always isLatest: false\n // once deleted, see markDeletedDraftPublishVersionsAsNotLatest — stay visible\n // when showSoftDeleted=inclusive/exclusive calls qb.withDeleted() to bypass\n // TypeORM's automatic deletedAt-IS-NULL exclusion. Under the default query\n // (no withDeleted()), deletedAt IS NOT NULL rows are already excluded by\n // TypeORM itself, so this OR has no effect there.\n qb.andWhere(\n `(${entityAlias}.isLatest = :defaultIsLatest OR ${entityAlias}.deletedAt IS NOT NULL)`,\n { defaultIsLatest: true },\n );\n }\n }\n\n filtersContainField(filters: any, fieldName: string): boolean {\n return this.someFilterLeaf(filters, fieldName, () => true);\n }\n\n async copyPublishedVersionAndUpdate<T extends CommonEntity>(\n context: DraftPublishCrudContext<T>,\n id: number,\n updateDto: any,\n files: Express.Multer.File[],\n isPartialUpdate: boolean,\n model: ModelMetadata,\n ): Promise<T> {\n const relationNames = this.getVersionedRelationNames(model);\n const sourceEntity = await context.repo.findOne({\n where: { id } as unknown as FindOptionsWhere<T>,\n relations: relationNames as any,\n });\n\n if (!sourceEntity) {\n throw new Error(`Entity [${context.moduleName}.${context.modelName}] with id ${id} not found`);\n }\n\n let hasMediaFields = false;\n // Snapshot of what the caller actually submitted, used only for hasOwnProperty checks\n // below; the transform pipeline works off its own copy (transformedDto) so this\n // reference never needs to be cloned.\n const submittedDto = updateDto;\n let transformedDto = { ...updateDto };\n\n for (const field of model.fields) {\n const transformed = await context.validateAndTransformDto(field, transformedDto, files, hasMediaFields, isPartialUpdate, false);\n transformedDto = transformed.dto;\n hasMediaFields = transformed.hasMediaFields;\n }\n\n const newVersionPayload = this.buildNewVersionPayload(context.repo, sourceEntity, relationNames);\n\n for (const field of model.fields) {\n if (this.wasFieldSubmitted(submittedDto, field) && Object.prototype.hasOwnProperty.call(transformedDto, field.name)) {\n newVersionPayload[field.name] = transformedDto[field.name];\n }\n }\n\n const chainId = sourceEntity.initialEntityVersionId || sourceEntity.id;\n newVersionPayload.initialEntityVersionId = chainId;\n newVersionPayload.isLatest = true;\n newVersionPayload.isPublished = false;\n newVersionPayload.publishedAt = null;\n newVersionPayload.publishedTracker = this.createTemporaryPublishedVersionTracker();\n\n const savedVersion = await context.repo.manager.transaction(async (manager) => {\n const transactionalRepo = manager.getRepository(context.repo.metadata.target);\n\n await manager\n .createQueryBuilder()\n .update(context.repo.metadata.target)\n .set({ isLatest: false } as any)\n .where(`${context.escapeDatabaseName(context.getColumnDatabaseName('isLatest'))} = :isLatest`, { isLatest: true })\n .andWhere(`(${context.escapeDatabaseName(context.getColumnDatabaseName('initialEntityVersionId'))} = :chainId OR ${context.escapeDatabaseName(context.getColumnDatabaseName('id'))} = :chainId)`, { chainId })\n .callListeners(false)\n .execute();\n\n const newVersion = transactionalRepo.create(newVersionPayload);\n let persistedVersion = await transactionalRepo.save(newVersion) as unknown as T;\n persistedVersion.publishedTracker = this.createPublishedVersionTracker(persistedVersion.id);\n persistedVersion = await transactionalRepo.save(persistedVersion as any) as unknown as T;\n\n await this.cloneMediaForVersion(context, model, sourceEntity.id, persistedVersion, files, manager);\n\n return persistedVersion;\n });\n\n if (hasMediaFields) {\n await context.saveMedia(model, files, savedVersion);\n }\n\n return savedVersion;\n }\n\n assertDraftPublishDeleteAllowed<T extends CommonEntity>(\n modelName: string,\n entities: T[],\n ): void {\n // The caller just loaded these entities, so their isPublished flags are already\n // current — no need to re-query the DB for the same rows.\n const currentPublishedEntityIds = entities\n .filter(entity => entity.isPublished === true)\n .map(entity => entity.id);\n\n if (currentPublishedEntityIds.length > 0) {\n throw new BadRequestException(\n `Published ${modelName} record cannot be deleted. Publish another draft or unpublish it before deleting. Invalid Ids ${currentPublishedEntityIds.join(', ')}.`\n );\n }\n }\n\n markDeletedDraftPublishVersionsAsNotLatest<T extends CommonEntity>(model: ModelMetadata, entities: T[]): void {\n if (!this.isDraftPublishEnabled(model)) return;\n for (const entity of entities) {\n if (entity.isLatest === true) {\n entity.isLatest = false;\n }\n }\n }\n\n getLatestDraftPublishChainIds<T extends CommonEntity>(entities: T[]): number[] {\n return Array.from(new Set(\n entities\n .filter(entity => entity.isLatest === true)\n .map(entity => entity.initialEntityVersionId || entity.id)\n ));\n }\n\n getEntityIds<T extends CommonEntity>(entities: T[]): number[] {\n return entities.map(entity => entity.id).filter(Boolean);\n }\n\n async promoteLatestDraftPublishVersionAfterDelete<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n deletedLatestChainIds: number[],\n deletedIds: number[],\n ): Promise<void> {\n if (deletedLatestChainIds.length === 0) return;\n\n // Chains are independent of one another, so promote replacements concurrently\n // rather than awaiting one chain at a time.\n await Promise.all(deletedLatestChainIds.map(chainId =>\n this.promoteReplacementForChain(repo, chainId, deletedIds)\n ));\n }\n\n private async promoteReplacementForChain<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n chainId: number,\n deletedIds: number[],\n ): Promise<void> {\n const replacementQuery = repo.manager\n .createQueryBuilder(repo.metadata.target, 'entity')\n .where('(entity.initialEntityVersionId = :chainId OR entity.id = :chainId)', { chainId })\n .orderBy('entity.createdAt', 'DESC')\n .addOrderBy('entity.id', 'DESC');\n\n if (deletedIds.length > 0) {\n replacementQuery.andWhere('entity.id NOT IN (:...deletedIds)', { deletedIds });\n }\n\n const replacement = await replacementQuery.getOne() as T | null;\n if (!replacement) return;\n\n await repo.manager\n .createQueryBuilder()\n .update(repo.metadata.target)\n .set({ isLatest: false } as any)\n .where('(initial_entity_version_id = :chainId OR id = :chainId)', { chainId })\n .execute();\n\n await repo.update(replacement.id, { isLatest: true } as unknown as QueryDeepPartialEntity<T>);\n }\n\n async promoteRecoveredDraftPublishVersion<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n recoveredEntity: T,\n ): Promise<void> {\n const chainId = recoveredEntity.initialEntityVersionId || recoveredEntity.id;\n\n await repo.manager\n .createQueryBuilder()\n .update(repo.metadata.target)\n .set({ isLatest: false } as any)\n .where('(initial_entity_version_id = :chainId OR id = :chainId)', { chainId })\n .execute();\n\n await repo.update(recoveredEntity.id, { isLatest: true } as unknown as QueryDeepPartialEntity<T>);\n recoveredEntity.isLatest = true;\n }\n\n async publishRecord<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n model: ModelMetadata,\n modelName: string,\n id: number,\n activeUser?: any,\n ): Promise<T> {\n const entity = await this.loadLatestEntityForPublishAction(repo, model, modelName, id, 'published');\n\n if (entity.isPublished) {\n throw new BadRequestException(`${modelName} with id ${id} is already published`);\n }\n\n const chainId = entity.initialEntityVersionId || entity.id;\n\n return repo.manager.transaction(async (manager) => {\n const transactionalRepo = manager.getRepository(repo.metadata.target);\n const chainVersions = await transactionalRepo.find({\n where: [\n { initialEntityVersionId: chainId },\n { id: chainId },\n ] as any,\n }) as unknown as T[];\n const archivedVersions = chainVersions\n .filter(version => version.id !== entity.id)\n .map(version => ({\n ...version,\n isPublished: false,\n publishedTracker: this.createPublishedVersionTracker(version.id),\n }));\n\n if (archivedVersions.length > 0) {\n await transactionalRepo.save(archivedVersions as any);\n }\n\n return transactionalRepo.save({\n ...entity,\n initialEntityVersionId: chainId,\n isPublished: true,\n publishedAt: new Date(),\n publishedTracker: \"na\",\n updatedBy: activeUser?.sub ?? entity.updatedBy,\n } as any) as Promise<T>;\n });\n }\n\n async unpublishRecord<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n model: ModelMetadata,\n modelName: string,\n id: number,\n activeUser?: any,\n ): Promise<T> {\n const entity = await this.loadLatestEntityForPublishAction(repo, model, modelName, id, 'unpublished');\n\n if (!entity.isPublished) {\n throw new BadRequestException(`${modelName} with id ${id} is already unpublished`);\n }\n\n return repo.save({\n ...entity,\n isPublished: false,\n updatedBy: activeUser?.sub ?? entity.updatedBy,\n });\n }\n\n /**\n * Load the latest version of a draft/publish-enabled record for a publish/unpublish\n * action, applying the two checks both actions share (workflow enabled, version is latest).\n */\n private async loadLatestEntityForPublishAction<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n model: ModelMetadata,\n modelName: string,\n id: number,\n action: 'published' | 'unpublished',\n ): Promise<T> {\n this.assertDraftPublishWorkflowEnabled(model, modelName);\n\n const entity = await repo.findOne({ where: { id } as any });\n if (!entity) {\n throw new NotFoundException(`${modelName} with id ${id} not found`);\n }\n\n if (entity.isLatest === false) {\n throw new BadRequestException(`Only the latest version of ${modelName} can be ${action}.`);\n }\n\n return entity;\n }\n\n /**\n * Walk a (possibly nested, $and/$or-combined) filters object looking for any leaf keyed\n * by `fieldName`, delegating the leaf test to the caller. Shared by filtersContainField\n * (any occurrence counts) and filtersContainBooleanValue (occurrence must match a value).\n */\n private someFilterLeaf(filters: any, fieldName: string, matchesLeaf: (value: any) => boolean): boolean {\n if (!filters || typeof filters !== 'object') return false;\n const normalizedFilters = normalizeObjectKeys(filters);\n\n return Object.keys(normalizedFilters).some(key => {\n const [rawField] = key.split(':');\n const value = normalizedFilters[key];\n if (rawField === fieldName) return matchesLeaf(value);\n if (key === '$and' || key === '$or') {\n return Array.isArray(value) && value.some((nestedFilter: any) => this.someFilterLeaf(nestedFilter, fieldName, matchesLeaf));\n }\n return value && typeof value === 'object' && this.someFilterLeaf(value, fieldName, matchesLeaf);\n });\n }\n\n private normalizeBooleanFilterValue(value: boolean | string | undefined): boolean | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n const normalized = value.toLowerCase();\n if (normalized === 'true') return true;\n if (normalized === 'false') return false;\n }\n return undefined;\n }\n\n private filtersContainBooleanValue(filters: any, fieldName: string, expectedValue: boolean): boolean {\n return this.someFilterLeaf(filters, fieldName, value => this.extractBooleanFilterValues(value).includes(expectedValue));\n }\n\n private extractBooleanFilterValues(filterValue: any): boolean[] {\n if (filterValue === undefined || filterValue === null) return [];\n const directValue = this.normalizeBooleanFilterValue(filterValue);\n if (directValue !== undefined) return [directValue];\n if (Array.isArray(filterValue)) {\n return filterValue\n .map(value => this.normalizeBooleanFilterValue(value))\n .filter((value): value is boolean => value !== undefined);\n }\n if (typeof filterValue !== 'object') return [];\n\n const normalizedValue = normalizeObjectKeys(filterValue);\n return Object.keys(normalizedValue)\n .filter(operator => operator === '$eq' || operator === '$in')\n .flatMap(operator => this.extractBooleanFilterValues(normalizedValue[operator]));\n }\n\n private getVersionedRelationNames(model: ModelMetadata): string[] {\n return model.fields\n .filter(field => field.type === SolidFieldType.relation && field.relationType !== RelationType.oneToMany)\n .map(field => field.name);\n }\n\n private wasFieldSubmitted(dto: any, field: FieldMetadata): boolean {\n if (!dto) return false;\n if (Object.prototype.hasOwnProperty.call(dto, field.name)) return true;\n if (field.type !== SolidFieldType.relation) return false;\n if (Object.prototype.hasOwnProperty.call(dto, `${field.name}Id`)) return true;\n if (Object.prototype.hasOwnProperty.call(dto, `${field.name}Ids`)) return true;\n if (Object.prototype.hasOwnProperty.call(dto, `${field.name}UserKey`)) return true;\n if (Object.prototype.hasOwnProperty.call(dto, `${field.name}Command`)) return true;\n if (field.relationCoModelFieldName) {\n if (Object.prototype.hasOwnProperty.call(dto, `${field.relationCoModelFieldName}Ids`)) return true;\n if (Object.prototype.hasOwnProperty.call(dto, `${field.relationCoModelFieldName}Command`)) return true;\n }\n return false;\n }\n\n private buildNewVersionPayload<T extends CommonEntity>(\n repo: SolidBaseRepository<T>,\n sourceEntity: T,\n relationNames: string[],\n ): any {\n const newVersionPayload: any = {};\n const excludedColumns = new Set([\n 'id',\n 'createdAt',\n 'updatedAt',\n 'deletedAt',\n 'createdBy',\n 'updatedBy',\n 'publishedAt',\n ...DRAFT_PUBLISH_VERSIONING_FIELD_NAMES,\n ]);\n\n repo.metadata.columns.forEach(column => {\n if (!excludedColumns.has(column.propertyName)) {\n newVersionPayload[column.propertyName] = sourceEntity[column.propertyName];\n }\n });\n\n for (const relationName of relationNames) {\n newVersionPayload[relationName] = sourceEntity[relationName];\n }\n\n return newVersionPayload;\n }\n\n private async cloneMediaForVersion<T extends CommonEntity>(\n context: DraftPublishCrudContext<T>,\n model: ModelMetadata,\n sourceEntityId: number,\n targetEntity: T,\n files: Express.Multer.File[] = [],\n entityManager?: EntityManager,\n ): Promise<void> {\n const mediaFields = model.fields.filter(field => field.type === SolidFieldType.mediaSingle || field.type === SolidFieldType.mediaMultiple);\n if (mediaFields.length === 0) return;\n\n const uploadedFieldNames = new Set(files.map(file => file.fieldname));\n const clonableFieldIds = mediaFields\n .filter(field => !uploadedFieldNames.has(field.name))\n .map(field => field.id);\n if (clonableFieldIds.length === 0) return;\n\n const mediaRepository = (entityManager ?? context.getDatasourceDefaultEntityManager()).getRepository(Media);\n\n // One query for every clonable media field instead of one query per field.\n const sourceMedia = await mediaRepository.find({\n where: {\n entityId: sourceEntityId,\n modelMetadata: { id: model.id },\n fieldMetadata: { id: In(clonableFieldIds) },\n } as any,\n relations: {\n modelMetadata: true,\n mediaStorageProviderMetadata: true,\n fieldMetadata: true,\n } as any,\n });\n if (sourceMedia.length === 0) return;\n\n // Reuse the same underlying file across versions instead of physically duplicating it\n // in storage; MediaService.deletePhysicalFile guards against removing the file while\n // another Media row still references it. Saved as a single batch instead of one insert\n // per row.\n const clonedMedia = sourceMedia.map(media => mediaRepository.create({\n entityId: targetEntity.id,\n relativeUri: media.relativeUri,\n fileSize: media.fileSize,\n mimeType: media.mimeType,\n originalFileName: media.originalFileName,\n modelMetadata: media.modelMetadata,\n mediaStorageProviderMetadata: media.mediaStorageProviderMetadata,\n fieldMetadata: media.fieldMetadata,\n }));\n await mediaRepository.save(clonedMedia);\n }\n\n private createPublishedVersionTracker(seed: number | string): string {\n return `version:${seed}`;\n }\n\n private createTemporaryPublishedVersionTracker(): string {\n return `version:pending:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n }\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { EntityManager } from "typeorm";
|
|
2
|
+
import { CommonEntity } from "../entities/common.entity";
|
|
3
|
+
import { ModelMetadata } from "../entities/model-metadata.entity";
|
|
4
|
+
import { SolidRegistry } from "../helpers/solid-registry";
|
|
5
|
+
import { SolidBaseRepository } from "../repository/solid-base.repository";
|
|
6
|
+
export declare class InternationalisationHelperService {
|
|
7
|
+
private readonly logger;
|
|
8
|
+
isInternationalisationEnabled(model: ModelMetadata): boolean;
|
|
9
|
+
applyCreateDefaults(model: ModelMetadata, createDto: any, solidRegistry: SolidRegistry): void;
|
|
10
|
+
deleteChildLocaleEntities<T extends CommonEntity>(repo: SolidBaseRepository<T>, model: ModelMetadata, entityIds: number[]): Promise<void>;
|
|
11
|
+
pickCurrentLocaleRecord(records: any[], localeName: string, draftPublishEnabled: boolean): any | null;
|
|
12
|
+
resolveDefaultLocaleName(solidRegistry: SolidRegistry): string;
|
|
13
|
+
getEntityRecordsInAllLocales(entityManager: EntityManager, modelName: string, id: string, defaultEntityLocaleIdFromQuery: string | undefined, draftPublishEnabled: boolean): Promise<{
|
|
14
|
+
records: any[];
|
|
15
|
+
defaultEntityLocaleId: string | null;
|
|
16
|
+
}>;
|
|
17
|
+
buildApplicableLocales(entityManager: EntityManager, modelName: string, id: string, defaultEntityLocaleIdFromQuery: string | undefined, draftPublishEnabled: boolean): Promise<any[]>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=internationalisation-helper.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internationalisation-helper.service.d.ts","sourceRoot":"","sources":["../../src/services/internationalisation-helper.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAM,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE1E,qBAAa,iCAAiC;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsD;IAE7E,6BAA6B,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAK5D,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,aAAa,GAAG,IAAI;IAKvF,yBAAyB,CAAC,CAAC,SAAS,YAAY,EAClD,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAC5B,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC;IAehB,uBAAuB,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,mBAAmB,EAAE,OAAO,GAAG,GAAG,GAAG,IAAI;IAarG,wBAAwB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM;IAKxD,4BAA4B,CAC9B,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,8BAA8B,EAAE,MAAM,GAAG,SAAS,EAClD,mBAAmB,EAAE,OAAO,GAC7B,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IA4D9D,sBAAsB,CACxB,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,8BAA8B,EAAE,MAAM,GAAG,SAAS,EAClD,mBAAmB,EAAE,OAAO,GAC7B,OAAO,CAAC,GAAG,EAAE,CAAC;CAgBpB"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InternationalisationHelperService = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const typeorm_1 = require("typeorm");
|
|
6
|
+
const locale_entity_1 = require("../entities/locale.entity");
|
|
7
|
+
const string_helper_1 = require("../helpers/string.helper");
|
|
8
|
+
class InternationalisationHelperService {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.logger = new common_1.Logger(InternationalisationHelperService.name);
|
|
11
|
+
}
|
|
12
|
+
isInternationalisationEnabled(model) {
|
|
13
|
+
return model?.internationalisation === true;
|
|
14
|
+
}
|
|
15
|
+
applyCreateDefaults(model, createDto, solidRegistry) {
|
|
16
|
+
if (!this.isInternationalisationEnabled(model) || createDto.localeName)
|
|
17
|
+
return;
|
|
18
|
+
createDto.localeName = this.resolveDefaultLocaleName(solidRegistry);
|
|
19
|
+
}
|
|
20
|
+
async deleteChildLocaleEntities(repo, model, entityIds) {
|
|
21
|
+
if (!this.isInternationalisationEnabled(model) || entityIds.length === 0)
|
|
22
|
+
return;
|
|
23
|
+
const childEntities = await repo.find({
|
|
24
|
+
where: { defaultEntityLocaleId: (0, typeorm_1.In)(entityIds) },
|
|
25
|
+
});
|
|
26
|
+
if (childEntities.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
if (model.enableSoftDelete === true) {
|
|
29
|
+
await repo.softRemove(childEntities);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
await repo.remove(childEntities);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
pickCurrentLocaleRecord(records, localeName, draftPublishEnabled) {
|
|
36
|
+
const localeRecords = records.filter(record => record.localeName === localeName);
|
|
37
|
+
if (localeRecords.length === 0)
|
|
38
|
+
return null;
|
|
39
|
+
return localeRecords.sort((a, b) => {
|
|
40
|
+
if (draftPublishEnabled && Boolean(a.isLatest) !== Boolean(b.isLatest))
|
|
41
|
+
return a.isLatest ? -1 : 1;
|
|
42
|
+
return Number(b.id ?? 0) - Number(a.id ?? 0);
|
|
43
|
+
})[0];
|
|
44
|
+
}
|
|
45
|
+
resolveDefaultLocaleName(solidRegistry) {
|
|
46
|
+
const defaultLocale = solidRegistry.getDefaultLocale();
|
|
47
|
+
return defaultLocale?.locale ?? 'en';
|
|
48
|
+
}
|
|
49
|
+
async getEntityRecordsInAllLocales(entityManager, modelName, id, defaultEntityLocaleIdFromQuery, draftPublishEnabled) {
|
|
50
|
+
const currentEntityRepository = entityManager.getRepository((0, string_helper_1.classify)(modelName));
|
|
51
|
+
if (id === 'new' && !defaultEntityLocaleIdFromQuery) {
|
|
52
|
+
this.logger.debug(`Creating new record without cloning from any defaultEntityLocaleId.`);
|
|
53
|
+
return { records: [], defaultEntityLocaleId: null };
|
|
54
|
+
}
|
|
55
|
+
if (id === 'new' && defaultEntityLocaleIdFromQuery) {
|
|
56
|
+
this.logger.debug(`Creating new record by cloning translations from defaultEntityLocaleId: ${defaultEntityLocaleIdFromQuery}`);
|
|
57
|
+
const records = await currentEntityRepository.find({
|
|
58
|
+
where: [
|
|
59
|
+
{ defaultEntityLocaleId: defaultEntityLocaleIdFromQuery },
|
|
60
|
+
{ id: defaultEntityLocaleIdFromQuery },
|
|
61
|
+
...(draftPublishEnabled ? [{ initialEntityVersionId: defaultEntityLocaleIdFromQuery }] : []),
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
this.logger.debug(`Found ${records.length} cloned records for new entity.`);
|
|
65
|
+
return { records, defaultEntityLocaleId: defaultEntityLocaleIdFromQuery };
|
|
66
|
+
}
|
|
67
|
+
const entityRecord = await currentEntityRepository.findOne({ where: { id } });
|
|
68
|
+
if (!entityRecord) {
|
|
69
|
+
this.logger.warn(`No entity found for id ${id}`);
|
|
70
|
+
return { records: [], defaultEntityLocaleId: null };
|
|
71
|
+
}
|
|
72
|
+
const defaultEntityLocaleId = entityRecord.defaultEntityLocaleId
|
|
73
|
+
|| (draftPublishEnabled ? entityRecord.initialEntityVersionId : null)
|
|
74
|
+
|| entityRecord.id;
|
|
75
|
+
if (entityRecord.defaultEntityLocaleId) {
|
|
76
|
+
this.logger.debug(`Editing translated locale record. Translation root id: ${defaultEntityLocaleId}`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
this.logger.debug(`Editing translation root record with id ${defaultEntityLocaleId}`);
|
|
80
|
+
}
|
|
81
|
+
const records = await currentEntityRepository.find({
|
|
82
|
+
where: [
|
|
83
|
+
{ defaultEntityLocaleId: defaultEntityLocaleId },
|
|
84
|
+
{ id: defaultEntityLocaleId },
|
|
85
|
+
...(draftPublishEnabled ? [{ initialEntityVersionId: defaultEntityLocaleId }] : []),
|
|
86
|
+
]
|
|
87
|
+
});
|
|
88
|
+
this.logger.debug(`Found ${records.length} records in all locales for existing entity.`);
|
|
89
|
+
return { records, defaultEntityLocaleId };
|
|
90
|
+
}
|
|
91
|
+
async buildApplicableLocales(entityManager, modelName, id, defaultEntityLocaleIdFromQuery, draftPublishEnabled) {
|
|
92
|
+
const { records: entityRecordsInAllLocales, defaultEntityLocaleId } = await this.getEntityRecordsInAllLocales(entityManager, modelName, id, defaultEntityLocaleIdFromQuery, draftPublishEnabled);
|
|
93
|
+
const allLocales = await entityManager.getRepository(locale_entity_1.Locale).find({});
|
|
94
|
+
return allLocales.map(locale => {
|
|
95
|
+
const matchingRecord = this.pickCurrentLocaleRecord(entityRecordsInAllLocales, locale.locale, draftPublishEnabled);
|
|
96
|
+
return {
|
|
97
|
+
locale: locale.locale,
|
|
98
|
+
displayName: locale.displayName,
|
|
99
|
+
isDefault: locale.isDefault ? 'yes' : 'no',
|
|
100
|
+
defaultEntityLocaleId,
|
|
101
|
+
entityId: matchingRecord ? matchingRecord.id : null,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.InternationalisationHelperService = InternationalisationHelperService;
|
|
107
|
+
//# sourceMappingURL=internationalisation-helper.service.js.map
|