UNPKG

@itwin/core-backend

Version:
270 lines • 13.9 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Schema */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Entity = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const ecschema_metadata_1 = require("@itwin/ecschema-metadata"); const Symbols_1 = require("./internal/Symbols"); /** Represents one of the fundamental building block in an [[IModelDb]]: as an [[Element]], [[Model]], or [[Relationship]]. * Every subclass of Entity represents one BIS [ECClass]($ecschema-metadata). * An Entity is typically instantiated from an [EntityProps]($common) and can be converted back to this representation via [[Entity.toJSON]]. * @public @preview */ class Entity { /** An immutable property used to discriminate between [[Entity]] and [EntityProps]($common), used to inform the TypeScript compiler that these two types * are never substitutable for one another. To obtain an EntityProps from an Entity, use [[Entity.toJSON]]. */ isInstanceOfEntity = true; /** The Schema that defines this class. */ static schema; // TODO: Schema key on the static level, but it requires a version which may differ between imodels get _ctor() { return this.constructor; } /** The name of the BIS class associated with this class. * @note Every subclass of Entity **MUST** override this method to identify its BIS class. * Failure to do so will ordinarily result in an error when the class is registered, since there may only * be one JavaScript class for a given BIS class (usually the errant class will collide with its superclass.) */ static get className() { return "Entity"; } /** Serves as a unique identifier for this class. Typed variant of [[classFullName]]. * @public @preview */ static get schemaItemKey() { // We cannot cache this here because the className gets overridden in subclasses return new ecschema_metadata_1.SchemaItemKey(this.className, this.schema.schemaKey); } /** Cached Metadata for the ECClass */ _metadata; /** When working with an Entity it can be useful to set property values directly, bypassing the compiler's type checking. * This property makes such code slightly less tedious to read and write. * @internal */ get asAny() { return this; } /** The name of the BIS Schema that defines this class */ get schemaName() { return this._ctor.schema.schemaName; } /** The name of the BIS class associated with this class. */ get className() { return this._ctor.className; } /** The [[IModelDb]] that contains this Entity */ iModel; /** The Id of this Entity. May be invalid if the Entity has not yet been saved in the database. */ id; constructor(props, iModel) { this.iModel = iModel; this.id = core_bentley_1.Id64.fromJSON(props.id); // copy all auto-handled properties from input to the object being constructed // eslint-disable-next-line @typescript-eslint/no-deprecated this.forEachProperty((propName, meta) => this[propName] = meta.createProperty(props[propName]), false); } /** Invoke the constructor of the specified `Entity` subclass. * @internal */ static instantiate(subclass, props, iModel) { return new subclass(props, iModel); } /** List of properties that are need to be custom handled during deserialization and serialization. * These properties differ between the ECSql instance of an Entity and the Entity itself. * @beta */ static _customHandledProps = [ { propertyName: "id", source: "Class" }, { propertyName: "className", source: "Class" }, { propertyName: "jsonProperties", source: "Class" } ]; /** Get the list of properties that are custom handled by this class and its superclasses. * @internal */ static getCustomHandledProperties() { if (this.name === "Entity") { return this._customHandledProps; } const superClass = Object.getPrototypeOf(this); return [ ...superClass.getCustomHandledProperties(), ...this._customHandledProps, ]; } /** Converts an ECSqlRow of an Entity to an EntityProps. This is used to deserialize an Entity from the database. * @beta */ static deserialize(props) { const enProps = { classFullName: props.row.classFullName, id: props.row.id, }; // Handles cases where id64 ints are stored in the jsonProperties and converts them to hex before parsing as a json object in js if (props.row.jsonProperties) { enProps.jsonProperties = JSON.parse(props.iModel[Symbols_1._nativeDb].patchJsonProperties(props.row.jsonProperties)); } // Auto handles all properties that are not in the 'customHandledProperties' list const customHandledProperties = this.getCustomHandledProperties(); Object.keys(props.row) .filter((propertyName) => customHandledProperties.find((val) => val.propertyName === propertyName) === undefined) .forEach((propertyName) => enProps[propertyName] = props.row[propertyName]); // Handles custom relClassNames to use '.' instead of ':' Object.keys(enProps).forEach((propertyName) => { if (enProps[propertyName].relClassName !== undefined && propertyName !== "modeledElement" && propertyName !== "parentModel") { enProps[propertyName].relClassName = enProps[propertyName].relClassName.replace(':', '.'); } }); return enProps; } /** Converts an EntityProps to an ECSqlRow. This is used to serialize an Entity to prepare to write it to the database. * @beta */ static serialize(props, _iModel) { const inst = { classFullName: props.classFullName, id: props.id, }; const customHandledProperties = this.getCustomHandledProperties(); Object.keys(props) .filter((propertyName) => customHandledProperties.find((val) => val.propertyName === propertyName) === undefined) .forEach((propertyName) => inst[propertyName] = props[propertyName]); return inst; } /** Obtain the JSON representation of this Entity. Subclasses of [[Entity]] typically override this method to return their corresponding sub-type of [EntityProps]($common) - * for example, [[GeometricElement.toJSON]] returns a [GeometricElementProps]($common). */ toJSON() { const val = {}; val.classFullName = this.classFullName; if (core_bentley_1.Id64.isValid(this.id)) val.id = this.id; // eslint-disable-next-line @typescript-eslint/no-deprecated this.forEachProperty((propName) => val[propName] = this[propName], false); return val; } /** Call a function for each property of this Entity. * @param func The callback to be invoked on each property * @param includeCustom If true (default), include custom-handled properties in the iteration. Otherwise, skip custom-handled properties. * @note Custom-handled properties are core properties that have behavior enforced by C++ handlers. * @deprecated in 5.0 - will not be removed until after 2026-06-13. Please use `forEach` to get the metadata and iterate over the properties instead. * * @example * ```typescript * // Deprecated method * entity.forEachProperty((name, propMetaData) => { * console.log(`Property name: ${name}, Property type: ${propMetaData.primitiveType}`); * }); * * // New method * entity.forEach((name, property) => { * console.log(`Property name: ${name}, Property type: ${property.propertyType}`); * }); * ``` */ // eslint-disable-next-line @typescript-eslint/no-deprecated forEachProperty(func, includeCustom = true) { // eslint-disable-next-line @typescript-eslint/no-deprecated this.iModel.forEachMetaData(this.classFullName, true, func, includeCustom); } /** * Call a function for each property of this Entity. * @param func The callback to be invoked on each property. * @param includeCustom If true (default), include custom-handled properties in the iteration. Otherwise, skip custom-handled properties. * @note Custom-handled properties are core properties that have behavior enforced by C++ handlers. * @throws Error if metadata for the class cannot be retrieved. * * @example * ```typescript * entity.forEach((name, property) => { * console.log(`Property name: ${name}, Property type: ${property.propertyType}`); * }); * ``` */ forEach(func, includeCustom = true) { const item = this._metadata ?? this.iModel.schemaContext.getSchemaItemSync(this.schemaItemKey); if (ecschema_metadata_1.EntityClass.isEntityClass(item) || ecschema_metadata_1.RelationshipClass.isRelationshipClass(item)) { for (const property of item.getPropertiesSync()) { if (includeCustom || !property.customAttributes?.has(`BisCore.CustomHandledProperty`)) func(property.name, property); } } else { throw new Error(`Cannot get metadata for ${this.classFullName}. Class is not an EntityClass or RelationshipClass.`); } } /** Get the full BIS class name of this Entity in the form "schema:class" */ static get classFullName() { return `${this.schema.schemaName}:${this.className}`; } /** Get the full BIS class name of this Entity in the form "schema:class". */ get classFullName() { return this._ctor.classFullName; } /** * Get the item key used by the ecschema-metadata package to identify this entity class * @public @preview */ get schemaItemKey() { return this._ctor.schemaItemKey; } /** Query metadata for this entity class from the iModel's schema. Returns cached metadata if available. * @throws [[IModelError]] if there is a problem querying the schema * @returns The metadata for the current entity * @public @preview */ async getMetaData() { if (this._metadata) { return this._metadata; } const ecClass = await this.iModel.schemaContext.getSchemaItem(this.schemaItemKey, ecschema_metadata_1.ECClass); if (ecschema_metadata_1.EntityClass.isEntityClass(ecClass) || ecschema_metadata_1.RelationshipClass.isRelationshipClass(ecClass)) { this._metadata = ecClass; return this._metadata; } else { throw new Error(`Cannot get metadata for ${this.classFullName}`); } } /** @internal */ getMetaDataSync() { if (this._metadata) { return this._metadata; } const ecClass = this.iModel.schemaContext.getSchemaItemSync(this.schemaItemKey, ecschema_metadata_1.ECClass); if (ecschema_metadata_1.EntityClass.isEntityClass(ecClass) || ecschema_metadata_1.RelationshipClass.isRelationshipClass(ecClass)) { this._metadata = ecClass; return this._metadata; } else { throw new Error(`Cannot get metadata for ${this.classFullName}`); } } /** @internal */ static get protectedOperations() { return []; } /** return whether this Entity class is a subclass of another Entity class * @note the subclass-ness is checked according to JavaScript inheritance, to check the underlying raw EC class's * inheritance, you can use [ECClass.is]($ecschema-metadata) * @note this should have a type of `is<T extends typeof Entity>(otherClass: T): this is T` but can't because of * typescript's restriction on the `this` type in static methods */ static is(otherClass) { // inline of @itwin/core-bentley's isSubclassOf due to protected constructor. return this === otherClass || this.prototype instanceof otherClass; } /** whether this JavaScript class was generated for this ECClass because there was no registered custom implementation * ClassRegistry overrides this when generating a class * @internal */ static get isGeneratedClass() { return false; } /** Get the set of this entity's *entity references*, [EntityReferenceSet]($backend). An *entity reference* is any id * stored on the entity, in its EC properties or json fields. * This is important for cloning operations but can be useful in other situations as well. * @see this.collectReferenceIds * @beta */ getReferenceIds() { const referenceIds = new core_common_1.EntityReferenceSet(); this.collectReferenceIds(referenceIds); return referenceIds; } /** Collect the Ids of this entity's *references* at this level of the class hierarchy. * A *reference* is any entity referenced by this entity's EC Data, including json fields. * This is important for cloning operations but can be useful in other situations as well. * @param _referenceIds The Id64Set to populate with reference Ids. * @note This should be overridden (with `super` called) at each level the class hierarchy that introduces references. * @see getReferenceIds * @beta */ collectReferenceIds(_referenceIds) { return; // no references by default } } exports.Entity = Entity; //# sourceMappingURL=Entity.js.map