UNPKG

@itwin/core-backend

Version:
148 lines • 7.96 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 Elements */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createChannelControl = createChannelControl; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const ChannelControl_1 = require("../ChannelControl"); const Element_1 = require("../Element"); const IModelHost_1 = require("../IModelHost"); const NavigationRelationship_1 = require("../NavigationRelationship"); const Symbols_1 = require("./Symbols"); const semver = require("semver"); class ChannelAdmin { _iModel; static channelClassName = "bis:ChannelRootAspect"; [Symbols_1._implementationProhibited] = undefined; _allowedChannels = new Set(); _allowedModels = new Set(); _deniedModels = new Map(); constructor(_iModel) { this._iModel = _iModel; // for backwards compatibility, allow the shared channel unless explicitly turned off in IModelHostOptions. if (IModelHost_1.IModelHost.configuration?.allowSharedChannel !== false) this._allowedChannels.add(ChannelControl_1.ChannelControl.sharedChannelName); } addAllowedChannel(channelKey) { this._allowedChannels.add(channelKey); this._deniedModels.clear(); } removeAllowedChannel(channelKey) { this._allowedChannels.delete(channelKey); this._allowedModels.clear(); } getChannelKey(elementId) { if (elementId === core_common_1.IModel.rootSubjectId) return ChannelControl_1.ChannelControl.sharedChannelName; try { const channel = this._iModel.withQueryReader(`SELECT Owner FROM ${ChannelAdmin.channelClassName} WHERE Element.Id=?`, (reader) => { return reader.step() ? reader.current[0] : undefined; }, new core_common_1.QueryBinder().bindId(1, elementId)); if (channel !== undefined) return channel; } catch { // Exception happens if the iModel is too old: ChannelRootAspect class not present in the BisCore schema (older than v1.0.10). // In that case all data in such iModel is assumed to be in the shared channel. return ChannelControl_1.ChannelControl.sharedChannelName; } const parentId = this._iModel.withPreparedSqliteStatement("SELECT ParentId,ModelId FROM bis_Element WHERE id=?", (stmt) => { stmt.bindId(1, elementId); if (core_bentley_1.DbResult.BE_SQLITE_ROW !== stmt.step()) throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, "Element does not exist"); return stmt.getValueId(0) ?? stmt.getValueId(1); // if parent is undefined, use modelId }); return this.getChannelKey(parentId); } [Symbols_1._verifyChannel](modelId) { // Note: indirect changes are permitted to change any channel if (this._allowedModels.has(modelId) || this._iModel[Symbols_1._nativeDb].getTxnMode() === "indirect") return; const deniedChannel = this._deniedModels.get(modelId); if (undefined !== deniedChannel) core_common_1.ChannelControlError.throwError("not-allowed", `Channel ${deniedChannel} is not allowed`, deniedChannel); const channel = this.getChannelKey(modelId); if (this._allowedChannels.has(channel)) { this._allowedModels.add(modelId); return; } this._deniedModels.set(modelId, channel); return this[Symbols_1._verifyChannel](modelId); } makeChannelRoot(args) { const txn = args.txn ?? this._iModel[Symbols_1._implicitTxn]; const channelKey = this.getChannelKey(args.elementId); if (ChannelControl_1.ChannelControl.sharedChannelName !== channelKey) core_common_1.ChannelControlError.throwError("may-not-nest", `Channel ${channelKey} may not nest`, channelKey); if (this.queryChannelRoot(args.channelKey) !== undefined) core_common_1.ChannelControlError.throwError("root-exists", `Channel ${args.channelKey} root already exist`, channelKey); const props = { classFullName: ChannelAdmin.channelClassName, element: { id: args.elementId, relClassName: NavigationRelationship_1.ElementOwnsChannelRootAspect.classFullName, }, owner: args.channelKey, }; txn.insertAspect(props); } insertChannelSubject(args) { const txn = args.txn ?? this._iModel[Symbols_1._implicitTxn]; // Check if channelKey already exists before inserting Subject. // makeChannelRoot will check that again, but at that point the new Subject is already inserted. // Prefer to check twice instead of deleting the Subject in the latter option. if (this.queryChannelRoot(args.channelKey) !== undefined) core_common_1.ChannelControlError.throwError("root-exists", `Channel ${args.channelKey} root already exist`, args.channelKey); const elementId = Element_1.Subject.insert(txn, args.parentSubjectId ?? core_common_1.IModel.rootSubjectId, args.subjectName, args.description); this.makeChannelRoot({ elementId, channelKey: args.channelKey, txn }); return elementId; } queryChannelRoot(channelKey) { if (channelKey === ChannelControl_1.ChannelControl.sharedChannelName) // RootSubject acts as the ChannelRoot element of the shared channel return core_common_1.IModel.rootSubjectId; try { const channelRoot = this._iModel.withQueryReader(`SELECT Element.Id FROM ${ChannelAdmin.channelClassName} WHERE Owner=?`, (reader) => { return reader.step() ? reader.current[0] : undefined; }, new core_common_1.QueryBinder().bindString(1, channelKey)); return channelRoot; } catch { // Exception happens if the iModel is too old: ChannelRootAspect class not present in the BisCore schema (older than v1.0.10). // In that case all data in such iModel is assumed to be in the shared channel. return undefined; } } async upgradeChannel(options, iModel, data) { // Validations if (!this._allowedChannels.has(options.channelKey)) core_common_1.ChannelControlError.throwError("not-allowed", `Channel ${options.channelKey} is not allowed`, options.channelKey); if (semver.gte(options.fromVersion, options.toVersion)) core_common_1.ChannelControlError.throwError("not-allowed", `Upgrading channel ${options.channelKey} from ${options.fromVersion} to ${options.toVersion} is not allowed`, options.channelKey); if (!this.queryChannelRoot(options.channelKey) && options.channelKey !== ChannelControl_1.ChannelControl.sharedChannelName) core_common_1.ChannelControlError.throwError("not-allowed", `Channel ${options.channelKey} not found`, options.channelKey); const context = { iModel, channelKey: options.channelKey, fromVersion: options.fromVersion, toVersion: options.toVersion, data, }; try { await options.callback(context); } catch (error) { core_common_1.ChannelControlError.throwError(error, "channel-upgrade-failed", `Channel ${options.channelKey} upgrade failed: ${error.message}`); } } } function createChannelControl(iModel) { return new ChannelAdmin(iModel); } //# sourceMappingURL=ChannelAdmin.js.map