@itwin/core-common
Version:
iTwin.js components common to frontend and backend
194 lines • 8.95 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 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