From 531603ad9ce6aa350b6ef67548f951fdf51e7922 Mon Sep 17 00:00:00 2001 From: Saulius Skliutas <24278440+saskliutas@users.noreply.github.com> Date: Tue, 30 Mar 2021 16:30:49 +0300 Subject: [PATCH 1/3] Add Ipc handler to delete/update element --- .../src/backend/SampleIpcHandler.ts | 31 +++++++++++ .../src/backend/electron/ElectronMain.ts | 2 + .../src/common/SampleIpcInterface.ts | 17 +++++++ .../src/frontend/api/MyAppFrontend.ts | 51 +++++++++++++++++-- 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts create mode 100644 test-apps/presentation-test-app/src/common/SampleIpcInterface.ts diff --git a/test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts b/test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts new file mode 100644 index 000000000000..e0dbc954e6ce --- /dev/null +++ b/test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import { Id64Arg } from "@bentley/bentleyjs-core"; +import { IModelDb, IpcHandler } from "@bentley/imodeljs-backend"; +import { ElementProps } from "@bentley/imodeljs-common"; +import { PRESENTATION_TEST_APP_IPC_CHANNEL_NAME, SampleIpcInterface } from "../common/SampleIpcInterface"; + +/** @internal */ +export class SampleIpcHandler extends IpcHandler implements SampleIpcInterface { + public channelName = PRESENTATION_TEST_APP_IPC_CHANNEL_NAME; + + public async deleteElements(imodelKey: string, elementIds: Id64Arg): Promise { + const iModelDb = IModelDb.tryFindByKey(imodelKey); + if (!iModelDb) + return; + + iModelDb.elements.deleteElement(elementIds); + iModelDb.saveChanges(); + } + + public async updateElement(imodelKey: string, newProps: ElementProps): Promise { + const iModelDb = IModelDb.tryFindByKey(imodelKey); + if (!iModelDb) + return; + + iModelDb.elements.updateElement(newProps); + iModelDb.saveChanges(); + } +} diff --git a/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts b/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts index 879554ca48e2..8bd3670d09bc 100644 --- a/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts +++ b/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts @@ -5,6 +5,7 @@ import * as path from "path"; import { ElectronHost, ElectronHostOptions } from "@bentley/electron-manager/lib/ElectronBackend"; import { RpcInterfaceDefinition } from "@bentley/imodeljs-common"; +import { SampleIpcHandler } from "../SampleIpcHandler"; /** * Initializes Electron backend @@ -18,6 +19,7 @@ export default async function initialize(rpcInterfaces: RpcInterfaceDefinition[] webResourcesPath: path.join(__dirname, "..", "..", "..", "build"), rpcInterfaces, developmentServer: process.env.NODE_ENV === "development", + ipcHandlers: [SampleIpcHandler], }; await ElectronHost.startup({ electronHost }); diff --git a/test-apps/presentation-test-app/src/common/SampleIpcInterface.ts b/test-apps/presentation-test-app/src/common/SampleIpcInterface.ts new file mode 100644 index 000000000000..5443f7f6fb89 --- /dev/null +++ b/test-apps/presentation-test-app/src/common/SampleIpcInterface.ts @@ -0,0 +1,17 @@ + +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import { Id64Arg } from "@bentley/bentleyjs-core"; +import { ElementProps } from "@bentley/imodeljs-common"; + +/** @internal */ +export const PRESENTATION_TEST_APP_IPC_CHANNEL_NAME = "presentation-test-app-ipc-interface"; + +/** @internal */ +export interface SampleIpcInterface { + updateElement(imodelKey: string, newProps: ElementProps): Promise; + deleteElements(imodelKey: string, elementIds: Id64Arg): Promise; +} diff --git a/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts b/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts index b7e7343605a8..769cca8960e6 100644 --- a/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts +++ b/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts @@ -2,9 +2,10 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { Guid, Logger } from "@bentley/bentleyjs-core"; -import { ViewQueryParams } from "@bentley/imodeljs-common"; -import { IModelConnection, SnapshotConnection } from "@bentley/imodeljs-frontend"; +import { Guid, Id64Arg, Logger, OpenMode } from "@bentley/bentleyjs-core"; +import { ElementProps, IModelError, ViewQueryParams } from "@bentley/imodeljs-common"; +import { AsyncMethodsOf, BriefcaseConnection, IModelConnection, IpcApp, PromiseReturnType, SnapshotConnection } from "@bentley/imodeljs-frontend"; +import { PRESENTATION_TEST_APP_IPC_CHANNEL_NAME, SampleIpcInterface } from "../../common/SampleIpcInterface"; import SampleRpcInterface from "../../common/SampleRpcInterface"; export class MyAppFrontend { @@ -19,8 +20,17 @@ export class MyAppFrontend { } public static async openIModel(path: string): Promise { - this.iModel = await SnapshotConnection.openFile(path); - Logger.logInfo("presentation", `Opened: ${this.iModel.name}`); + if (IpcApp.isValid) { + Logger.logInfo("presentation", `Trying to open standalone ${path}`); + this.iModel = await tryOpenStandalone(path); + } + + if (!this.iModel) { + Logger.logInfo("presentation", `Opening snapshot: ${path}`); + this.iModel = await SnapshotConnection.openFile(path); + Logger.logInfo("presentation", `Opened snapshot: ${this.iModel.name}`); + } + return this.iModel; } @@ -45,4 +55,35 @@ export class MyAppFrontend { label: spec.userLabel ?? spec.code.value!, })); } + + public static async updateElement(imodel: IModelConnection, newProps: ElementProps) { + if (!IpcApp.isValid) + return; + return this.callIpc("updateElement", imodel.key, newProps); + } + + public static async deleteElements(imodel: IModelConnection, elementIds: Id64Arg) { + if (!IpcApp.isValid) + return; + return this.callIpc("deleteElements", imodel.key, elementIds); + } + + private static async callIpc>(methodName: T, ...args: Parameters): Promise> { + return IpcApp.callIpcChannel(PRESENTATION_TEST_APP_IPC_CHANNEL_NAME, methodName, ...args); + } +} + +async function tryOpenStandalone(path: string) { + let iModel: IModelConnection | undefined; + try { + iModel = await BriefcaseConnection.openStandalone(path, OpenMode.ReadWrite); + Logger.logInfo("presentation", `Opened standalone: ${iModel.name}`); + } catch (err) { + if (err instanceof IModelError) { + Logger.logError("presentation", `Failed to open standalone: ${err.message}`, err.getMetaData); + } else { + Logger.logError("presentation", `Failed to open standalone.`); + } + } + return iModel; } From 2a8ff8fdd285459f6ca00605542f2381da0de474 Mon Sep 17 00:00:00 2001 From: Saulius Skliutas <24278440+saskliutas@users.noreply.github.com> Date: Tue, 30 Mar 2021 18:27:33 +0300 Subject: [PATCH 2/3] Initialize presentation-backed in ReadWrite mode --- test-apps/presentation-test-app/src/backend/main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-apps/presentation-test-app/src/backend/main.ts b/test-apps/presentation-test-app/src/backend/main.ts index 52f7adddc709..a999d7d09905 100644 --- a/test-apps/presentation-test-app/src/backend/main.ts +++ b/test-apps/presentation-test-app/src/backend/main.ts @@ -34,11 +34,12 @@ import { PresentationBackendLoggerCategory, PresentationBackendNativeLoggerCateg Presentation.initialize({ rulesetDirectories: [path.join("assets", "presentation_rules")], localeDirectories: [path.join("assets", "locales")], - mode: PresentationManagerMode.ReadOnly, + mode: PresentationManagerMode.ReadWrite, taskAllocationsMap: { [RequestPriority.Max]: 1, }, useMmap: true, + updatesPollInterval: 20, }); // __PUBLISH_EXTRACT_END__ From 2476802f7bacc9f119ae13ebc049e9cf4245f069 Mon Sep 17 00:00:00 2001 From: Saulius Skliutas <24278440+saskliutas@users.noreply.github.com> Date: Tue, 30 Mar 2021 18:27:59 +0300 Subject: [PATCH 3/3] Throw error when using ipc calls in non IpcApp --- .../presentation-test-app/src/frontend/api/MyAppFrontend.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts b/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts index 769cca8960e6..053a1e4f22a8 100644 --- a/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts +++ b/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts @@ -58,13 +58,13 @@ export class MyAppFrontend { public static async updateElement(imodel: IModelConnection, newProps: ElementProps) { if (!IpcApp.isValid) - return; + throw new Error(`Updating element only supported in 'IpcApp'`); return this.callIpc("updateElement", imodel.key, newProps); } public static async deleteElements(imodel: IModelConnection, elementIds: Id64Arg) { if (!IpcApp.isValid) - return; + throw new Error(`Deleting elements only supported in 'IpcApp'`); return this.callIpc("deleteElements", imodel.key, elementIds); }