diff --git a/src/app/core/entity/entity-config.service.spec.ts b/src/app/core/entity/entity-config.service.spec.ts index d4d6a4b572..8d8ec3149e 100644 --- a/src/app/core/entity/entity-config.service.spec.ts +++ b/src/app/core/entity/entity-config.service.spec.ts @@ -14,6 +14,7 @@ import { EntityMapperService } from "./entity-mapper/entity-mapper.service"; import { mockEntityMapper } from "./entity-mapper/mock-entity-mapper-service"; import { EntityConfig } from "./entity-config"; import { EntitySchemaField } from "./schema/entity-schema-field"; +import { Child } from "../../child-dev-project/children/model/child"; describe("EntityConfigService", () => { let service: EntityConfigService; @@ -82,6 +83,32 @@ describe("EntityConfigService", () => { expect(Test2.schema).toHaveKey(ATTRIBUTE_2_NAME); }); + it("should reset attribute to basic class config if custom attribute disappears from config doc", () => { + const originalLabel = Child.schema.get("name").label; + const customLabel = "custom label"; + + const mockEntityConfigs: (EntityConfig & { _id: string })[] = [ + { + _id: "entity:Child", + attributes: { name: { label: customLabel } }, + }, + ]; + mockConfigService.getAllConfigs.and.returnValue(mockEntityConfigs); + service.setupEntitiesFromConfig(); + expect(Child.schema.get("name").label).toEqual(customLabel); + + mockConfigService.getAllConfigs.and.returnValue([ + { + _id: "entity:Child", + attributes: { + /* undo custom label */ + }, + }, + ]); + service.setupEntitiesFromConfig(); + expect(Child.schema.get("name").label).toEqual(originalLabel); + }); + it("should allow to configure the `.toString` method", () => { mockConfigService.getAllConfigs.and.returnValue([ { _id: "entity:Test", toStringAttributes: ["name", "entityId"] }, diff --git a/src/app/core/entity/entity-config.service.ts b/src/app/core/entity/entity-config.service.ts index 9bdc2acfbc..27c665c812 100644 --- a/src/app/core/entity/entity-config.service.ts +++ b/src/app/core/entity/entity-config.service.ts @@ -6,6 +6,8 @@ import { IconName } from "@fortawesome/fontawesome-svg-core"; import { EntityConfig } from "./entity-config"; import { addPropertySchema } from "./database-field.decorator"; import { PREFIX_VIEW_CONFIG } from "../config/dynamic-routing/view-config.interface"; +import { EntitySchemaField } from "./schema/entity-schema-field"; +import { EntitySchema } from "./schema/entity-schema"; /** * A service that allows to work with configuration-objects @@ -19,6 +21,9 @@ export class EntityConfigService { /** @deprecated will become private, use the service to access the data */ static readonly PREFIX_ENTITY_CONFIG = "entity:"; + /** original initial entity schemas without overrides from config */ + private coreEntitySchemas = new Map(); + static getDetailsViewId(entityConstructor: EntityConstructor) { return ( PREFIX_VIEW_CONFIG + entityConstructor.route.replace(/^\//, "") + "/:id" @@ -30,7 +35,21 @@ export class EntityConfigService { constructor( private configService: ConfigService, private entities: EntityRegistry, - ) {} + ) { + this.storeCoreEntitySchemas(); + } + + private storeCoreEntitySchemas() { + this.entities.forEach((ctr, key) => { + this.coreEntitySchemas.set(key, this.deepCopySchema(ctr.schema)); + }); + } + + private deepCopySchema(schema: EntitySchema): EntitySchema { + return new Map( + JSON.parse(JSON.stringify(Array.from(schema))), + ); + } /** * Assigns additional schema-fields to all entities that are @@ -49,6 +68,7 @@ export class EntityConfigService { this.createNewEntity(id, config.extends); } const ctor = this.entities.get(id); + this.setCoreSchemaAttributes(ctor, config.extends); this.addConfigAttributes(ctor, config); } } @@ -58,14 +78,37 @@ export class EntityConfigService { ? this.entities.get(parent) : Entity; + const schema = this.deepCopySchema(parentClass.schema); class DynamicClass extends parentClass { - static schema = new Map(parentClass.schema.entries()); + static schema = schema; static ENTITY_TYPE = id; } this.entities.set(id, DynamicClass); } + /** + * Set field definitons from the core schema to ensure undoing customized attributes is correctly applied. + * @param entityType + * @param parent + */ + private setCoreSchemaAttributes( + entityType: EntityConstructor, + parent: string, + ) { + const coreEntityId = parent ?? entityType.ENTITY_TYPE; + const coreSchema = + this.coreEntitySchemas.get(coreEntityId) ?? Entity.schema; + + for (const [key, value] of coreSchema.entries()) { + addPropertySchema( + entityType.prototype, + key, + JSON.parse(JSON.stringify(value)), + ); + } + } + /** * Appends the given (dynamic) attributes to the schema of the provided Entity. * If no arguments are provided, they will be loaded from the config