src/app/core/entity/model/entity.ts
"Entity" is a base class for all domain model classes. It implements the basic general properties and methods that are required for all Entity types e.g. supporting the Entity Schema system or basic database logic.
Entity classes do not deal with database actions, use EntityMapperService with its find/save/delete functions.
Do not use the Entity class directly. Instead, implement your own Entity types, writing classes that extend "Entity". A How-To Guide on how to implement your own types is available:
Properties |
|
Methods |
|
Accessors |
constructor(id: string)
|
||||||||
Defined in src/app/core/entity/model/entity.ts:264
|
||||||||
Creates an entity object with the given id. This id is final and won't be changeable after this object has been created.
Parameters :
|
Static Optional _isCustomizedType |
Type : boolean
|
Defined in src/app/core/entity/model/entity.ts:76
|
True if this type's schema has been customized dynamically from the config. |
_rev |
Type : string
|
Decorators :
@DatabaseField({anonymize: 'retain'})
|
Defined in src/app/core/entity/model/entity.ts:199
|
internal database doc revision, used to detect conflicts by PouchDB/CouchDB |
anonymized |
Type : boolean
|
Decorators :
@DatabaseField({anonymize: 'retain'})
|
Defined in src/app/core/entity/model/entity.ts:218
|
Whether this entity has been anonymized and therefore cannot be re-activated. |
Static color |
Type : string
|
Defined in src/app/core/entity/model/entity.ts:126
|
color used for to highlight this entity type across the app |
created |
Type : UpdateMetadata
|
Decorators :
@DatabaseField({anonymize: 'retain'})
|
Defined in src/app/core/entity/model/entity.ts:204
|
Static DATABASE |
Type : string
|
Default value : "app"
|
Defined in src/app/core/entity/model/entity.ts:63
|
The database where these entities are stored. |
Static ENTITY_TYPE |
Type : string
|
Default value : "Entity"
|
Defined in src/app/core/entity/model/entity.ts:58
|
The entity's type.
In classes extending Entity this is usually overridden by the class annotation |
Static hasPII |
Type : boolean
|
Default value : false
|
Defined in src/app/core/entity/model/entity.ts:189
|
whether this entity type can contain "personally identifiable information" (PII) and therefore should follow strict data protection requirements and offer a function to anonymize records. |
Static icon |
Type : IconName
|
Defined in src/app/core/entity/model/entity.ts:121
|
icon id used for this entity |
inactive |
Type : boolean
|
Decorators :
@DatabaseField({anonymize: 'retain'})
|
Defined in src/app/core/entity/model/entity.ts:212
|
Static label |
Type : string
|
Defined in src/app/core/entity/model/entity.ts:94
|
human-readable name/label of the entity in the UI |
Static schema |
Type : EntitySchema
|
Defined in src/app/core/entity/model/entity.ts:71
|
EntitySchema defining property transformations from/to the database.
This is auto-generated from the property annotations see /additional-documentation/how-to-guides/create-a-new-entity-type.html |
Static Optional toBlockDetailsAttributes |
Type : EntityBlockConfig
|
Defined in src/app/core/entity/model/entity.ts:89
|
Defining which attributes will be displayed in a tooltip on hover when the record is displayed as an entity-block. |
Static toStringAttributes |
Type : []
|
Default value : ["entityId"]
|
Defined in src/app/core/entity/model/entity.ts:84
|
Defining which attribute values of an entity should be shown in the The default is the ID of the entity ( |
updated |
Type : UpdateMetadata
|
Decorators :
@DatabaseField({anonymize: 'retain'})
|
Defined in src/app/core/entity/model/entity.ts:209
|
assertValid |
assertValid()
|
Defined in src/app/core/entity/model/entity.ts:385
|
Checks if the entity is valid and if the check fails, throws an error explaining the failed check.
Returns :
void
|
Public copy | ||||||||||
copy(newId: string | boolean)
|
||||||||||
Defined in src/app/core/entity/model/entity.ts:368
|
||||||||||
Shallow copy of the entity. The resulting entity will be of the same type as this (taking into account subclassing)
Parameters :
|
Static createPrefixedId | ||||||||||||
createPrefixedId(type: string, id: string)
|
||||||||||||
Defined in src/app/core/entity/model/entity.ts:174
|
||||||||||||
Create a prefixed id by adding the type prefix if it isn't already part of the given id.
Parameters :
Returns :
string
|
Static extractEntityIdFromId | ||||||||
extractEntityIdFromId(id: string)
|
||||||||
Defined in src/app/core/entity/model/entity.ts:158
|
||||||||
Extract entityId without prefix.
Parameters :
Returns :
string
|
Static extractTypeFromId | ||||||||
extractTypeFromId(id: string)
|
||||||||
Defined in src/app/core/entity/model/entity.ts:149
|
||||||||
Extract the ENTITY_TYPE from an id.
Parameters :
Returns :
string
|
Public getColor |
getColor()
|
Defined in src/app/core/entity/model/entity.ts:349
|
Used by some generic UI components to set the color for the entity instance. Override this method as needed.
Returns :
string
|
getConstructor |
getConstructor()
|
Defined in src/app/core/entity/model/entity.ts:279
|
Get the class (Entity or the actual subclass of the instance) to call static methods on the correct class considering inheritance
Returns :
EntityConstructor<>
|
Public getId | ||||||
getId(withoutPrefix)
|
||||||
Defined in src/app/core/entity/model/entity.ts:298
|
||||||
Returns the id of this entity. Note that an id is final and can't be changed after the object has been instantiated, hence there is no
Parameters :
Returns :
string
the unique id of this entity |
getSchema |
getSchema()
|
Defined in src/app/core/entity/model/entity.ts:286
|
Get the entity schema of this class
Returns :
EntitySchema
|
Public getType |
getType()
|
Defined in src/app/core/entity/model/entity.ts:309
|
Returns the type which is used to categorize this entity in the database. Important: Do not overwrite this method! Types are handled internally.
Returns :
string
the entity's type (which is the class name). |
Public getWarningLevel |
getWarningLevel()
|
Defined in src/app/core/entity/model/entity.ts:357
|
Override getWarningLevel() to define when the entity is in a critical condition and should be color-coded and highlighted in generic components of the UI.
Returns :
WarningLevel
|
labelPlural | ||||||
getlabelPlural()
|
||||||
Defined in src/app/core/entity/model/entity.ts:99
|
||||||
human-readable label for uses of plural of the entity in the UI
Returns :
string
|
||||||
setlabelPlural(value: string)
|
||||||
Defined in src/app/core/entity/model/entity.ts:103
|
||||||
Parameters :
Returns :
void
|
route | ||||||
getroute()
|
||||||
Defined in src/app/core/entity/model/entity.ts:131
|
||||||
Base route of the entity (list/details) view for this entity type.
Returns :
string
|
||||||
setroute(value: string)
|
||||||
Defined in src/app/core/entity/model/entity.ts:139
|
||||||
Parameters :
Returns :
void
|
isNew |
getisNew()
|
Defined in src/app/core/entity/model/entity.ts:221
|
whether this entity object is newly created and not yet saved to database
Returns :
boolean
|
isActive | ||||||
getisActive()
|
||||||
Defined in src/app/core/entity/model/entity.ts:247
|
||||||
Check, if this entity is considered active or archived. This is taken from the property "inactive".
If the property doesn't exist, the default is Some subclasses overwrite this functionality, but this logic is considered deprecated (!) now and implementations have to make sure that "inactive" property takes precedence!
Returns :
boolean
|
||||||
setisActive(isActive: boolean)
|
||||||
Defined in src/app/core/entity/model/entity.ts:261
|
||||||
If existing entities with
Parameters :
Returns :
void
|
import { v4 as uuid } from "uuid";
import { EntitySchema } from "../schema/entity-schema";
import { DatabaseField } from "../database-field.decorator";
import {
getWarningLevelColor,
WarningLevel,
} from "../../../child-dev-project/warning-level";
import { IconName } from "@fortawesome/fontawesome-svg-core";
import { UpdateMetadata } from "./update-metadata";
import { EntityBlockConfig } from "../../basic-datatypes/entity/entity-block/entity-block-config";
import { Logging } from "../../logging/logging.service";
/**
* This represents a static class of type <T>.
* It can be used for passing a class from which new objects should be created.
* It can also be used to check the ENTITY_TYPE of a class
* For example usage check the {@link EntityMapperService}.
*/
export type EntityConstructor<T extends Entity = Entity> = (new (
id?: string,
) => T) &
typeof Entity;
/**
* "Entity" is a base class for all domain model classes.
* It implements the basic general properties and methods that are required for all Entity types
* e.g. supporting the Entity Schema system or basic database logic.
*
* Entity classes do not deal with database actions, use {@link EntityMapperService} with its find/save/delete functions.
*
* Do not use the Entity class directly. Instead, implement your own Entity types, writing classes that extend "Entity".
* A How-To Guide on how to implement your own types is available:
* - [How to Create a new Entity Type]{@link /additional-documentation/how-to-guides/create-a-new-entity-type.html}
*/
export class Entity {
/**
* The entity's type.
* In classes extending Entity this is usually overridden by the class annotation `@DatabaseEntity('NewEntity')`.
* The type needs to be used as routing path in lower case. The routing path can be defined in the configuration file.
*/
static ENTITY_TYPE = "Entity";
/**
* The database where these entities are stored.
*/
static DATABASE = "app";
/**
* EntitySchema defining property transformations from/to the database.
* This is auto-generated from the property annotations `@DatabaseField()`.
*
* see {@link /additional-documentation/how-to-guides/create-a-new-entity-type.html}
*/
static schema: EntitySchema;
/**
* True if this type's schema has been customized dynamically from the config.
*/
static _isCustomizedType?: boolean; // todo should be private or renamed to "isCustomizedType"
/**
* Defining which attribute values of an entity should be shown in the `.toString()` method.
*
* The default is the ID of the entity (`entityId`).
* This can be overwritten in subclasses or through the config.
*/
static toStringAttributes = ["entityId"];
/**
* Defining which attributes will be displayed in a tooltip on hover when the record is displayed as an entity-block.
*/
static toBlockDetailsAttributes?: EntityBlockConfig;
/**
* human-readable name/label of the entity in the UI
*/
static label: string;
/**
* human-readable label for uses of plural of the entity in the UI
*/
static get labelPlural(): string {
return this._labelPlural ?? this.label;
}
static set labelPlural(value: string) {
this._labelPlural = value;
}
private static _labelPlural: string;
/**
* Returns a human-readable string representation of the entity *type*
* (not the individual record).
*/
static toString(plural: boolean = false): string {
const result = plural ? this.labelPlural : this.label;
return result ?? this.ENTITY_TYPE;
}
/**
* icon id used for this entity
*/
static icon: IconName;
/**
* color used for to highlight this entity type across the app
*/
static color: string;
/**
* Base route of the entity (list/details) view for this entity type.
*/
static get route(): string {
let route = this._route ?? this.ENTITY_TYPE.toLowerCase();
if (!route.startsWith("/")) {
route = "/" + route;
}
return route;
}
static set route(value: string) {
this._route = value;
}
private static _route: string;
/**
* Extract the ENTITY_TYPE from an id.
* @param id An entity's id including prefix.
*/
static extractTypeFromId(id: string): string {
const split = id.indexOf(":");
return id.substring(0, split);
}
/**
* Extract entityId without prefix.
* @param id An entity's id including prefix.
*/
static extractEntityIdFromId(id: string): string {
let type: string = undefined;
try {
const split = id.indexOf(":");
type = id.substring(split + 1);
} catch (e) {
Logging.debug("Error extracting entityId from id", id, e);
}
return type;
}
/**
* Create a prefixed id by adding the type prefix if it isn't already part of the given id.
* @param type The type prefix to be added.
* @param id The id to be extended with a prefix.
*/
static createPrefixedId(type: string, id: string): string {
id = String(id);
const prefix = type + ":";
if (!id.startsWith(prefix)) {
return prefix + id;
} else {
return id;
}
}
/**
* whether this entity type can contain "personally identifiable information" (PII)
* and therefore should follow strict data protection requirements
* and offer a function to anonymize records.
*/
static hasPII: boolean = false;
/**
* Internal database id.
* This is usually combined from the ENTITY_TYPE as a prefix with the entityId field `EntityType:entityId`
* @example "Entity:123"
*/
@DatabaseField({ anonymize: "retain" }) private _id: string;
/** internal database doc revision, used to detect conflicts by PouchDB/CouchDB */
@DatabaseField({ anonymize: "retain" }) _rev: string;
@DatabaseField({
anonymize: "retain",
})
created: UpdateMetadata;
@DatabaseField({
anonymize: "retain",
})
updated: UpdateMetadata;
@DatabaseField({ anonymize: "retain" })
inactive: boolean;
/**
* Whether this entity has been anonymized and therefore cannot be re-activated.
*/
@DatabaseField({ anonymize: "retain" })
anonymized: boolean;
/** whether this entity object is newly created and not yet saved to database */
get isNew(): boolean {
return !this._rev;
}
/** actual id without prefix */
private get entityId(): string {
return Entity.extractEntityIdFromId(this._id);
}
/**
* Set id without prefix.
* @param newEntityId The new id without prefix.
*/
private set entityId(newEntityId: string) {
this._id = Entity.createPrefixedId(this.getType(), newEntityId);
}
/**
* Check, if this entity is considered active or archived.
*
* This is taken from the property "inactive".
* If the property doesn't exist, the default is `true`.
*
* Some subclasses overwrite this functionality, but this logic is considered deprecated (!) now
* and implementations have to make sure that "inactive" property takes precedence!
*/
get isActive(): boolean {
if (this.inactive !== undefined) {
return !this.inactive;
}
if (this["active"] !== undefined) {
return this["active"];
}
return true;
}
/**
* If existing entities with `isActive: false` exist, then these values are assigned to the property "active".
* @param isActive
*/
set isActive(isActive: boolean) {
this["active"] = isActive;
this.inactive = !isActive;
}
/**
* Creates an entity object with the given id. This id is final and won't be changeable after this object has been
* created.
*
* @param id a unique id for this entity; if no id is passed a uuid is generated automatically
*/
constructor(id: string = uuid()) {
this.entityId = id;
}
/**
* Get the class (Entity or the actual subclass of the instance) to call static methods on the correct class considering inheritance
*/
getConstructor(): EntityConstructor<this> {
return this.constructor as EntityConstructor<this>;
}
/**
* Get the entity schema of this class
*/
getSchema(): EntitySchema {
return this.getConstructor().schema;
}
/**
* Returns the id of this entity.
*
* Note that an id is final and can't be changed after the object has been instantiated, hence there is no
* <code>setId()</code> method.
*
* @returns {string} the unique id of this entity
*/
public getId(withoutPrefix = false): string {
return withoutPrefix ? this.entityId : this._id;
}
/**
* Returns the type which is used to categorize this entity in the database.
*
* <b>Important: Do not overwrite this method! Types are handled internally.</b>
*
* @returns {string} the entity's type (which is the class name).
*/
public getType(): string {
return this.getConstructor().ENTITY_TYPE;
}
/**
* Returns a string representation or summary of the instance.
* This can be configured with the static `toStringAttributes` for each subclass.
*
* @returns {string} the instance's string representation.
*/
public toString(): string {
if (
this.anonymized &&
this.getConstructor().toStringAttributes.every(
(attr) => this[attr] === undefined,
)
) {
return $localize`:Entity.toString fallback for anonymized record:[anonymized ${
this.getConstructor().label
}]`;
}
return this.getConstructor()
.toStringAttributes.map((attr) => {
let value = this[attr];
if (value?.label) {
value = value.label;
}
if (value instanceof Date) {
value = value.toLocaleDateString();
}
return value;
})
.join(" ");
}
/**
* Used by some generic UI components to set the color for the entity instance.
* Override this method as needed.
*/
public getColor(): string {
return getWarningLevelColor(this.getWarningLevel());
}
/**
* Override getWarningLevel() to define when the entity is in a critical condition and should be color-coded
* and highlighted in generic components of the UI.
*/
public getWarningLevel(): WarningLevel {
return WarningLevel.NONE;
}
/**
* Shallow copy of the entity.
* The resulting entity will be of the same type as this
* (taking into account subclassing)
*
* @param newId if true, a new entityId will be generated; if a string, that value is used as new entityId
*/
public copy(newId: string | boolean = false): this {
const other = new (this.getConstructor())(this._id);
Object.assign(other, this);
if (newId) {
other.entityId = typeof newId === "string" ? newId : uuid();
delete other._rev;
delete other.created;
delete other.updated;
}
return other;
}
/**
* Checks if the entity is valid and if the check fails, throws an error explaining the failed check.
*/
assertValid(): void {
return;
}
}