@itwin/core-backend
Version:
iTwin.js backend components
148 lines • 6.71 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 SQLiteDb
*/
import { CloudSqlite } from "./CloudSqlite";
import { VersionedSqliteDb } from "./SQLiteDb";
import { BriefcaseDb, IModelDb } from "./IModelDb";
import { DbResult, OpenMode } from "@itwin/core-bentley";
import { IModelError } from "@itwin/core-common";
import { IModelNative } from "./internal/NativePlatform";
import { _nativeDb } from "./internal/Symbols";
/** @internal */
export var SchemaSync;
(function (SchemaSync) {
const lockParams = { retryDelayMs: 1000, nRetries: 30 };
/** A CloudSqlite database for synchronizing schema changes across briefcases. */
class SchemaSyncDb extends VersionedSqliteDb {
myVersion = "4.0.0";
createDDL() { }
}
SchemaSync.SchemaSyncDb = SchemaSyncDb;
const syncProperty = { namespace: "itwinjs", name: "SchemaSync" };
const defaultDbName = "SchemaSyncDb";
const testSyncCachePropKey = "test.schema_sync.cache_name";
// for tests only
SchemaSync.setTestCache = (iModel, cacheName) => {
if (cacheName)
iModel[_nativeDb].saveLocalValue(testSyncCachePropKey, cacheName);
else
iModel[_nativeDb].deleteLocalValue(testSyncCachePropKey);
};
const getCloudAccess = async (arg) => {
let nativeDb;
const argIsIModelDb = arg instanceof IModelDb;
if (argIsIModelDb) {
nativeDb = arg[_nativeDb];
}
else {
nativeDb = new IModelNative.platform.DgnDb();
nativeDb.openIModel(arg.fileName, OpenMode.Readonly);
}
const propsString = nativeDb.queryFileProperty(syncProperty, true);
if (!propsString)
throw new Error("iModel does not have a SchemaSyncDb");
try {
const props = JSON.parse(propsString);
const accessToken = await CloudSqlite.requestToken(props);
const access = new CloudAccess({ ...props, accessToken });
Object.assign(access.lockParams, lockParams);
const testSyncCache = nativeDb.queryLocalValue(testSyncCachePropKey);
if (testSyncCache)
access.setCache(CloudSqlite.CloudCaches.getCache({ cacheName: testSyncCache }));
return access;
}
finally {
if (!argIsIModelDb) {
nativeDb.closeFile();
}
}
};
SchemaSync.withLockedAccess = async (iModel, args, operation) => {
const access = await getCloudAccess(iModel);
try {
await access.withLockedDb(args, async () => operation(access));
}
finally {
access.close();
}
};
SchemaSync.withReadonlyAccess = async (iModel, operation) => {
const access = await getCloudAccess(iModel);
access.synchronizeWithCloud();
access.openForRead();
try {
await operation(access);
}
finally {
access.close();
}
};
SchemaSync.isEnabled = (iModel) => {
return iModel[_nativeDb].schemaSyncEnabled();
};
/** Synchronize local briefcase schemas with cloud container */
SchemaSync.pull = async (iModel) => {
if (iModel[_nativeDb].schemaSyncEnabled() && !iModel.isReadonly) {
await SchemaSync.withReadonlyAccess(iModel, async (syncAccess) => {
const schemaSyncDbUri = syncAccess.getUri();
iModel.clearCaches();
iModel[_nativeDb].schemaSyncPull(schemaSyncDbUri);
iModel.saveChanges("schema synchronized with cloud container");
});
}
};
SchemaSync.initializeForIModel = async (arg) => {
const props = { baseUri: arg.containerProps.baseUri, containerId: arg.containerProps.containerId, storageType: arg.containerProps.storageType }; // sanitize to only known properties
const iModel = arg.iModel;
const briefcase = iModel instanceof BriefcaseDb ? iModel : undefined;
await iModel.acquireSchemaLock();
if (briefcase) {
if (briefcase.txns.hasLocalChanges) {
throw new IModelError(DbResult.BE_SQLITE_ERROR, "Enabling SchemaSync for iModel failed. There are unsaved or un-pushed local changes.");
}
await briefcase.pullChanges();
}
try {
iModel.saveFileProperty(syncProperty, JSON.stringify(props));
await SchemaSync.withLockedAccess(arg.iModel, { operationName: "initialize schemaSync", openMode: OpenMode.Readonly }, async (syncAccess) => {
iModel[_nativeDb].schemaSyncInit(syncAccess.getUri(), props.containerId, arg.overrideContainer ?? false);
iModel.saveChanges(`Enable SchemaSync (container id: ${props.containerId})`);
});
}
catch (err) {
throw err;
}
finally {
iModel.abandonChanges();
}
if (briefcase) {
if (arg.overrideContainer)
await briefcase.pushChanges({ description: `Overriding SchemaSync for iModel with container-id: ${props.containerId}` });
else
await briefcase.pushChanges({ description: `Enable SchemaSync for iModel with container-id: ${props.containerId}` });
}
};
/** Provides access to a cloud-based `SchemaSyncDb` to hold ECSchemas. */
class CloudAccess extends CloudSqlite.DbAccess {
constructor(props) {
super({ dbType: SchemaSyncDb, props, dbName: defaultDbName });
}
getUri() {
return `${this.getCloudDb()[_nativeDb].getFilePath()}?vfs=${this.container.cache?.name}&writable=${this.container.isWriteable ? 1 : 0}`;
}
/**
* Initialize a cloud container for use as a SchemaSync. The container must first be created via its storage supplier api (e.g. Azure, or AWS).
* A valid sasToken that grants write access must be supplied. This function creates and uploads an empty ChannelDb into the container.
* @note this deletes any existing content in the container.
*/
static async initializeDb(props) {
return super._initializeDb({ props, dbType: SchemaSyncDb, dbName: defaultDbName });
}
}
SchemaSync.CloudAccess = CloudAccess;
})(SchemaSync || (SchemaSync = {}));
//# sourceMappingURL=SchemaSync.js.map