UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

139 lines • 7.41 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 RpcInterface */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BentleyCloudRpcProtocol = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const IModelError_1 = require("../../IModelError"); const RpcConfiguration_1 = require("../core/RpcConfiguration"); const RpcOperation_1 = require("../core/RpcOperation"); const WebAppRpcProtocol_1 = require("./WebAppRpcProtocol"); var AppMode; (function (AppMode) { AppMode["MilestoneReview"] = "1"; })(AppMode || (AppMode = {})); /** An http protocol for Bentley cloud RPC interface deployments. * @internal */ class BentleyCloudRpcProtocol extends WebAppRpcProtocol_1.WebAppRpcProtocol { checkToken = true; /** The name of various HTTP request headers based on client's request context */ serializedClientRequestContextHeaderNames = { /** The name of the HTTP request id header. */ id: "X-Correlation-Id", /** The name of the HTTP application id header. */ applicationId: "X-Application-Id", /** The name of the HTTP application version header. */ applicationVersion: "X-Application-Version", /** The name of the HTTP session id header. */ sessionId: "X-Session-Id", /** The name of the HTTP authorization header. */ authorization: "Authorization", }; /** The name of the RPC protocol version header. */ protocolVersionHeaderName = "X-Protocol-Version"; /** Returns the operation specified by an OpenAPI-compatible URI path. */ getOperationFromPath(path) { const url = new URL(path, "https://localhost/"); const components = url.pathname.split("/").filter((x) => x); // filter out empty segments const operationComponent = components.slice(-1)[0]; const encodedRequest = url.searchParams.get("parameters") || ""; // The encodedRequest should be base64 - fail now if any other characters detected. if (/[^a-zA-Z0-9=+\/$]/.test(encodedRequest)) throw new IModelError_1.IModelError(core_bentley_1.BentleyStatus.ERROR, `Invalid request: Malformed URL parameters detected.`); const firstHyphen = operationComponent.indexOf("-"); const lastHyphen = operationComponent.lastIndexOf("-"); const interfaceDefinition = operationComponent.slice(0, firstHyphen); const interfaceVersion = operationComponent.slice(firstHyphen + 1, lastHyphen); const operationName = operationComponent.slice(lastHyphen + 1); return { interfaceDefinition, operationName, interfaceVersion, encodedRequest }; } /** Supplies the OpenAPI-compatible URI path for an RPC operation. */ supplyPathForOperation(operation, request) { const prefix = this.pathPrefix; const appTitle = this.info.title; const appVersion = this.info.version; const operationId = `${operation.interfaceDefinition.interfaceName}-${operation.interfaceVersion}-${operation.operationName}`; let appMode = ""; let iTwinId = ""; let iModelId = ""; let routeChangesetId; /* Note: The changesetId field is omitted in the route in the case of ReadWrite connections since the connection is generally expected to be at the * latest version and not some specific changeset. Also, for the first version (before any changesets), the changesetId in the route is arbitrarily * set to "0" instead of an empty string, since the latter is more un-intuitive for a route. However, in all other use cases, including the changesetId * held by the IModelRpcProps itself, the changesetId of "" (i.e., empty string) signifies the first version - this is more intuitive and retains * compatibility with the majority of use cases. */ if (request === undefined) { appMode = "{modeId}"; iTwinId = "{iTwinId}"; iModelId = "{iModelId}"; routeChangesetId = "{changeSetId}"; } else { let token = operation.policy.token(request) || RpcOperation_1.RpcOperation.fallbackToken; if (!token || !token.iModelId) { if (RpcConfiguration_1.RpcConfiguration.disableRoutingValidation) { token = { key: "" }; } else { throw new IModelError_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "Invalid iModelToken for RPC operation request"); } } iTwinId = encodeURIComponent(token.iTwinId || ""); iModelId = encodeURIComponent(token.iModelId); routeChangesetId = token.changeset?.id || "0"; appMode = AppMode.MilestoneReview; } return `${prefix}/${appTitle}/${appVersion}/mode/${appMode}/context/${iTwinId}/imodel/${iModelId}${!!routeChangesetId ? `/changeset/${routeChangesetId}` : ""}/${operationId}`; } /** * Inflates the IModelRpcProps from the URL path for each request on the backend. * @note This function updates the IModelRpcProps value supplied in the request body. */ inflateToken(tokenFromBody, request) { const urlPathComponents = request.path.split("/"); let iModelId = tokenFromBody.iModelId; let iTwinId = tokenFromBody.iTwinId; const changeset = { id: tokenFromBody.changeset?.id ?? "0", index: tokenFromBody.changeset?.index }; for (let i = 0; i <= urlPathComponents.length; ++i) { const key = urlPathComponents[i]; const value = urlPathComponents[i + 1]; if (key === "mode") { ++i; } else if (key === "context") { iTwinId = value; ++i; } else if (key === "imodel") { iModelId = value; ++i; } else if (key === "changeset") { changeset.id = (value === "0") ? "" : value; ++i; } } // Overwrite the key if it includes a : because its most likely a guid. We know what it should be based off of the url. // Leave it alone if its a non guid key. return { key: tokenFromBody.key === undefined || tokenFromBody.key.includes(":") ? `${iModelId}:${changeset.id}` : tokenFromBody.key, iTwinId, iModelId, changeset }; } /** Returns the OpenAPI-compatible URI path parameters for an RPC operation. * @internal */ supplyPathParametersForOperation(_operation) { return [ { name: "modeId", in: "path", required: true, schema: { type: "string" } }, { name: "iTwinId", in: "path", required: true, schema: { type: "string" } }, { name: "iModelId", in: "path", required: true, schema: { type: "string" } }, { name: "changeSetId", in: "path", required: false, schema: { type: "string" } }, ]; } } exports.BentleyCloudRpcProtocol = BentleyCloudRpcProtocol; //# sourceMappingURL=BentleyCloudRpcProtocol.js.map