@itwin/core-backend
Version:
iTwin.js backend components
152 lines • 7.29 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Codes
*/
import { BentleyError, DbResult, Id64, IModelStatus } from "@itwin/core-bentley";
import { CodeSpec, IModelError } from "@itwin/core-common";
/** Manages [CodeSpecs]($docs/BIS/guide/fundamentals/element-fundamentals.md#codespec) within an [[IModelDb]]
* @public @preview
*/
export class CodeSpecs {
static tableName = "bis_CodeSpec";
_imodel;
_loadedCodeSpecs = [];
constructor(imodel) {
this._imodel = imodel;
if (imodel.isBriefcaseDb()) {
imodel.onChangesetApplied.addListener(() => this._loadedCodeSpecs.length = 0);
}
}
findByName(name) {
return this._imodel.withSqliteStatement(`SELECT Id FROM ${CodeSpecs.tableName} WHERE Name=?`, (stmt) => {
stmt.bindString(1, name);
return stmt.nextRow() ? stmt.getValueId(0) : undefined;
});
}
/** Look up the Id of the CodeSpec with the specified name. */
queryId(name) {
const id = this.findByName(name);
if (!id)
throw new IModelError(IModelStatus.NotFound, "CodeSpec not found");
return id;
}
/** Look up a CodeSpec by Id. The CodeSpec will be loaded from the database if necessary.
* @param codeSpecId The Id of the CodeSpec to load
* @returns The CodeSpec with the specified Id
* @throws [[IModelError]] if the Id is invalid or if no CodeSpec with that Id could be found.
*/
getById(codeSpecId) {
// good chance it is already loaded - check there before running a query
const found = this._loadedCodeSpecs.find((codeSpec) => codeSpec.id === codeSpecId);
if (found !== undefined)
return found;
// must load this codespec
const loadedCodeSpec = this.load(codeSpecId);
this._loadedCodeSpecs.push(loadedCodeSpec);
return loadedCodeSpec;
}
/** Returns true if the IModelDb has a CodeSpec of the specified Id. */
hasId(codeSpecId) {
try {
return undefined !== this.getById(codeSpecId);
}
catch {
return false;
}
}
/** Look up a CodeSpec by name. The CodeSpec will be loaded from the database if necessary.
* @param name The name of the CodeSpec to load
* @returns The CodeSpec with the specified name
* @throws [[IModelError]] if no CodeSpec with the specified name could be found.
*/
getByName(name) {
// good chance it is already loaded - check there before running a query
const found = this._loadedCodeSpecs.find((codeSpec) => codeSpec.name === name);
if (found !== undefined)
return found;
const codeSpecId = this.queryId(name);
if (codeSpecId === undefined)
throw new IModelError(IModelStatus.NotFound, "CodeSpec not found");
return this.getById(codeSpecId);
}
/** Returns true if the IModelDb has a CodeSpec of the specified name. */
hasName(name) {
try {
return undefined !== this.getByName(name);
}
catch {
return false;
}
}
insertCodeSpec(specName, properties) {
const iModel = this._imodel;
const spec = { name: specName.trim(), props: JSON.stringify(properties) };
if (this.findByName(spec.name))
throw new IModelError(IModelStatus.DuplicateName, "CodeSpec already exists");
const internalCodes = iModel.codeService?.internalCodes;
if (internalCodes) {
// Since there is no lock on the codespec table, to add a codespec to an iModel it must first be reserved in the
// internal code index via `internalCodes.reserveBisCodeSpecs` prior to calling this function.
// This ensures that the Ids will be unique, and the property values consistent, even if more than one user
// adds them without pushing their changes. The call to `verifyBisCodeSpec` will throw otherwise.
internalCodes.reader.verifyBisCodeSpec(spec);
}
else {
// If this iModel doesn't have an internal code index, we have no way of coordinating the Ids for CodeSpecs across multiple users.
// Just look in this briefcase to find the currently highest used Id and hope for the best.
spec.id = iModel.withSqliteStatement(`SELECT MAX(Id) FROM ${CodeSpecs.tableName}`, (stmt) => stmt.nextRow() ? stmt.getValueInteger(0) + 1 : 1);
}
const id = spec.id; // eslint-disable-line @typescript-eslint/no-non-null-assertion
iModel.withSqliteStatement(`INSERT INTO ${CodeSpecs.tableName}(Id,Name,JsonProperties) VALUES(?,?,?)`, (stmt) => {
stmt.bindInteger(1, id);
stmt.bindString(2, spec.name);
stmt.bindString(3, spec.props);
const rc = stmt.step();
if (rc !== DbResult.BE_SQLITE_DONE)
throw new BentleyError(rc, "Error inserting codeSpec");
});
return Id64.fromLocalAndBriefcaseIds(id, 0);
}
insert(codeSpecOrName, props) {
if (codeSpecOrName instanceof CodeSpec) {
const id = this.insertCodeSpec(codeSpecOrName.name, codeSpecOrName.properties);
codeSpecOrName.id = id;
return id;
}
if (props === undefined)
throw new IModelError(IModelStatus.BadArg, "Invalid argument");
if (typeof props === "object")
return this.insertCodeSpec(codeSpecOrName, props);
const spec = CodeSpec.create(this._imodel, codeSpecOrName, props);
return this.insertCodeSpec(spec.name, spec.properties);
}
/** Update the Json properties of an existing CodeSpec.
* @param codeSpec The codeSpec holding Json properties values to update
* @throws if unable to update the codeSpec.
*/
updateProperties(codeSpec) {
this._imodel.withSqliteStatement(`UPDATE ${CodeSpecs.tableName} SET JsonProperties=? WHERE Id=?`, (stmt) => {
stmt.bindString(1, JSON.stringify(codeSpec.properties));
stmt.bindId(2, codeSpec.id);
if (DbResult.BE_SQLITE_DONE !== stmt.step())
throw new IModelError(IModelStatus.BadArg, "error updating CodeSpec properties");
});
}
/** Load a CodeSpec from the iModel
* @param id The persistent Id of the CodeSpec to load
*/
load(id) {
if (Id64.isInvalid(id))
throw new IModelError(IModelStatus.InvalidId, "Invalid codeSpecId");
return this._imodel.withSqliteStatement(`SELECT Name,JsonProperties FROM ${CodeSpecs.tableName} WHERE Id=?`, (stmt) => {
stmt.bindId(1, id);
if (!stmt.nextRow())
throw new IModelError(IModelStatus.InvalidId, "CodeSpec not found");
return CodeSpec.createFromJson(this._imodel, id, stmt.getValueString(0), JSON.parse(stmt.getValueString(1)));
});
}
}
//# sourceMappingURL=CodeSpecs.js.map