UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

194 lines • 8.95 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module RpcInterface */ import { BentleyStatus } from "@itwin/core-bentley"; import { IModelError } from "../../IModelError"; import { RpcConfiguration } from "./RpcConfiguration"; import { RpcPendingQueue } from "./RpcPendingQueue"; import { initializeRpcRequest } from "./RpcRequest"; import { RpcRoutingToken } from "./RpcRoutingToken"; import { RpcInterface } from "../../RpcInterface"; import { RpcControlChannel } from "./RpcControl"; import { RpcOperation, RpcOperationPolicy } from "./RpcOperation"; /** @internal */ export const REGISTRY = Symbol.for("@itwin/core-common/RpcRegistry"); /** @internal */ export const OPERATION = Symbol.for("@itwin/core-common/RpcOperation"); /** @internal */ export const POLICY = Symbol.for("@itwin/core-common/RpcOperationPolicy"); /** @internal */ export const INSTANCE = Symbol.for("@itwin/core-common/RpcInterface/__instance__"); /** @internal */ export const CURRENT_REQUEST = Symbol.for("@itwin/core-common/RpcRequest/__current__"); /** @internal */ export const CURRENT_INVOCATION = Symbol.for("@itwin/core-common/RpcInvocation/__current__"); /** @internal */ export class RpcRegistry { static _instance; constructor() { } static get instance() { if (!RpcRegistry._instance) { const globalObj = typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}; if (!globalObj[REGISTRY]) globalObj[REGISTRY] = new RpcRegistry(); RpcRegistry._instance = globalObj[REGISTRY]; } return RpcRegistry._instance; } lookupInterfaceDefinition(name) { if (!this.definitionClasses.has(name)) throw new IModelError(BentleyStatus.ERROR, `RPC interface "${name}" is not initialized.`); return this.definitionClasses.get(name); } async describeAvailableEndpoints() { const requests = []; for (const channel of RpcControlChannel.channels) { requests.push(channel.describeEndpoints()); } const responses = await Promise.all(requests); const endpoints = responses.reduce((a, b) => a.concat(b), []); for (const endpoint of endpoints) { const definition = this.definitionClasses.get(endpoint.interfaceName); endpoint.compatible = (definition && RpcInterface.isVersionCompatible(endpoint.interfaceVersion, definition.interfaceVersion)) ? true : false; } return endpoints; } getClientForInterface(definition, routing = RpcRoutingToken.default) { let instance; const proxies = this.proxies.get(definition.interfaceName); if (proxies) { instance = proxies.get(routing.id); } if (!instance) instance = this.instantiateClient(definition, routing); return instance; } getImplForInterface(definition) { let instance = this.implementations.get(definition.interfaceName); if (!instance) instance = this.instantiateImpl(definition); return instance; } lookupImpl(interfaceName) { const definition = this.lookupInterfaceDefinition(interfaceName); return this.getImplForInterface(definition); } registerImpl(definition, implementation) { this.unregisterImpl(definition); this.implementationClasses.set(definition.interfaceName, implementation); } unregisterImpl(definition) { this.implementationClasses.delete(definition.interfaceName); const impl = this.implementations.get(definition.interfaceName); if (impl) { impl.configuration.onRpcImplTerminated(definition, impl); this.implementations.delete(definition.interfaceName); } } supplyImplInstance(definition, instance) { this.suppliedImplementations.set(definition.interfaceName, instance); } isRpcInterfaceInitialized(definition) { return this.definitionClasses.has(definition.interfaceName); } initializeRpcInterface(definition) { if (this.definitionClasses.has(definition.interfaceName)) { const existing = this.definitionClasses.get(definition.interfaceName); if (existing && definition.interfaceVersion === "CONTROL" && existing !== definition) { this.configureOperations(definition); // configs that differ only by routing still need the control ops initialized } return; } this.notifyInitialize(); this.definitionClasses.set(definition.interfaceName, definition); this.configureOperations(definition); } terminateRpcInterface(definition) { this.unregisterImpl(definition); this.purgeClient(definition); this.definitionClasses.delete(definition.interfaceName); } definitionClasses = new Map(); proxies = new Map(); implementations = new Map(); suppliedImplementations = new Map(); implementationClasses = new Map(); id = (() => { let i = 0; return () => ++i; })(); instantiateImpl(definition) { this.checkInitialized(definition); const registeredImplementation = this.implementationClasses.get(definition.interfaceName); if (!registeredImplementation) throw new IModelError(BentleyStatus.ERROR, `An RPC interface implementation class for "${definition.interfaceName}" is not registered.`); if (definition.prototype.configurationSupplier) registeredImplementation.prototype.configurationSupplier = definition.prototype.configurationSupplier; const supplied = this.suppliedImplementations.get(definition.interfaceName); const implementation = supplied || new registeredImplementation(); if (!(implementation instanceof registeredImplementation)) throw new IModelError(BentleyStatus.ERROR, `Invalid RPC interface implementation.`); if (supplied) { supplied.configuration = RpcConfiguration.supply(supplied); } this.implementations.set(definition.interfaceName, implementation); implementation.configuration.onRpcImplInitialized(definition, implementation); return implementation; } instantiateClient(definition, routing = RpcRoutingToken.default) { this.checkInitialized(definition); const proxy = new definition(routing); if (!this.proxies.has(definition.interfaceName)) { this.proxies.set(definition.interfaceName, new Map()); } this.proxies.get(definition.interfaceName)?.set(routing.id, proxy); Object.getOwnPropertyNames(definition.prototype).forEach((operationName) => { if (operationName === "constructor" || operationName === "configurationSupplier") return; this.interceptOperation(proxy, operationName); }); proxy.configuration.onRpcClientInitialized(definition, proxy); return proxy; } interceptOperation(proxy, operation) { const clientFunction = proxy[operation]; proxy[operation] = function () { const args = Array.from(arguments); args.push(operation); return clientFunction.apply(proxy, args); }; } checkInitialized(definition) { if (!this.definitionClasses.has(definition.interfaceName)) throw new IModelError(BentleyStatus.ERROR, `RPC interface "${definition.interfaceName}" is not initialized.`); } configureOperations(definition) { const proto = definition.prototype; Object.getOwnPropertyNames(proto).forEach((operationName) => { if (operationName === "constructor" || operationName === "configurationSupplier") return; const propertyName = RpcOperation.computeOperationName(operationName); if (!proto[propertyName][OPERATION]) { const policy = definition[POLICY] || new RpcOperationPolicy(); proto[propertyName][OPERATION] = new RpcOperation(definition, propertyName, policy); } }); } purgeClient(definition) { const proxies = this.proxies.get(definition.interfaceName); if (proxies) { proxies.forEach((proxy) => proxy.configuration.onRpcClientTerminated(definition, proxy)); this.proxies.delete(definition.interfaceName); } } notifyInitialize() { initializeRpcRequest(); RpcPendingQueue.initialize(); } } //# sourceMappingURL=RpcRegistry.js.map