UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

222 lines • 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IncrementalSchemaLocater = void 0; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ const Constants_1 = require("../Constants"); const SchemaGraphUtil_1 = require("../Deserialization/SchemaGraphUtil"); const ECObjects_1 = require("../ECObjects"); const Exception_1 = require("../Exception"); const Schema_1 = require("../Metadata/Schema"); const SchemaKey_1 = require("../SchemaKey"); const SchemaLoadingController_1 = require("../utils/SchemaLoadingController"); const IncrementalSchemaReader_1 = require("./IncrementalSchemaReader"); /** * A [[ISchemaLocater]] implementation for locating and retrieving EC [[Schema]] * objects incrementally instead of the full schema and it's references at once. This is useful for large schemas that * take a long time to load, but clients need a rough skeleton of the schema as fast as possible. * * The IncrementalSchemaLocater is a locater around the [[IncrementalSchemaLocater]] to be used in a * [[SchemaContext]]. * @internal */ class IncrementalSchemaLocater { _options; _schemaInfoCache; /** * Initializes a new instance of the IncrementalSchemaLocater class. * @param options The [[SchemaLocaterOptions]] that control the loading of the schema. */ constructor(options) { this._options = options || {}; this._schemaInfoCache = new SchemaInfoCache(async (context) => { return this.loadSchemaInfos(context); }); } /** Gets the options how the schema locater load the schemas. */ get options() { return this._options; } /** * Gets the [[SchemaInfo]] which matches the provided SchemaKey. The SchemaInfo may be returned * before the schema is fully loaded. May return the entire Schema so long as it is completely loaded as it satisfies * the SchemaInfo interface. * @param schemaKey The [[SchemaKey]] to look up. * @param matchType The [[SchemaMatchType]] to use against candidate schemas. * @param context The [[SchemaContext]] for loading schema references. */ async getSchemaInfo(schemaKey, matchType, context) { return this._schemaInfoCache.lookup(schemaKey, matchType, context); } /** * Attempts to get a [[Schema]] from the locater. Yields undefined if no matching schema is found. * For schemas that may have references, construct and call through a SchemaContext instead. * @param schemaKey The [[SchemaKey]] to look up. * @param matchType The [[SchemaMatchType]] to use against candidate schemas. * @param context The [[SchemaContext]] for loading schema references. */ async getSchema(schemaKey, matchType, context) { const schemaInfo = await this.getSchemaInfo(schemaKey, matchType, context); return schemaInfo ? this.loadSchema(schemaInfo, context) : undefined; } /** * Attempts to get a [[Schema]] from the locater. Yields undefined if no matching schema is found. * For schemas that may have references, construct and call through a SchemaContext instead. * NOT IMPLEMENTED IN THIS LOCATER - ALWAYS RETURNS UNDEFINED. * @param schemaKey The [[SchemaKey]] to look up. * @param matchType The [[SchemaMatchType]] to use against candidate schemas. * @param context The [[SchemaContext]] for loading schema references. * @returns Incremental schema loading does not work synchronously, this will always return undefined. */ getSchemaSync(_schemaKey, _matchType, _context) { return undefined; } /** * Start loading the schema for the given schema info incrementally. The schema is returned * as soon as the schema stub is loaded while the schema fully resolves in the background. It should * only be called by the IncrementalSchemaLocater if a schema has not been loaded or started to * load yet. * @param schemaInfo The schema info of the schema to load. * @param schemaContext The schema context to load the schema into. */ async loadSchema(schemaInfo, schemaContext) { // If the meta schema is an earlier version than 4.0.3, we can't use the ECSql query interface to get the schema // information required to load the schema entirely. In this case, we fallback to use the ECSchema RPC interface // to fetch the whole schema json. if (!await this.supportPartialSchemaLoading(schemaContext)) { const schemaJson = await this.getSchemaJson(schemaInfo.schemaKey, schemaContext); if (schemaJson === undefined) throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.UnableToLocateSchema, `Could not locate the schema, ${schemaInfo.schemaKey.name}.${schemaInfo.schemaKey.version.toString()}`); return Schema_1.Schema.fromJson(schemaJson, schemaContext); } // Fetches the schema partials for the given schema key. The first item in the array is the // actual schema props of the schema to load, the following items are schema props of referenced // schema items that are needed to resolve the schema properly. const schemaPartials = await this.getSchemaPartials(schemaInfo.schemaKey, schemaContext); if (schemaPartials === undefined) throw new Exception_1.ECSchemaError(Exception_1.ECSchemaStatus.UnableToLocateSchema, `Could not locate the schema, ${schemaInfo.schemaKey.name}.${schemaInfo.schemaKey.version.toString()}`); // Sort the partials in dependency order to ensure referenced schemas exist in the schema context // when they get requested. Otherwise the context would call this method again and for referenced // schemas, we would not want to request the whole schema props. const sortedPartials = await this.sortSchemaPartials(schemaPartials, schemaContext); for (const schemaProps of sortedPartials) { await this.startLoadingPartialSchema(schemaProps, schemaContext); } const schema = await schemaContext.getCachedSchema(schemaInfo.schemaKey); if (!schema) throw new Error(`Schema ${schemaInfo.schemaKey.name} could not be found.`); return schema; } /** * Creates a SchemaProps object by loading the Schema information from the given SchemaContext. * @param schemaKey The SchemaKey of the Schema whose props are to be retrieved. * @param schemaContext The SchemaContext holding the Schema. * @returns The SchemaProps object. */ async createSchemaProps(schemaKey, schemaContext) { const schemaInfo = await schemaContext.getSchemaInfo(schemaKey, ECObjects_1.SchemaMatchType.Latest); if (!schemaInfo) throw new Error(`Schema ${schemaKey.name} could not be found.`); const schemaReferences = []; const schemaProps = { $schema: Constants_1.ECSchemaNamespaceUris.SCHEMAURL3_2_JSON, name: schemaKey.name, alias: schemaInfo.alias, version: schemaInfo.schemaKey.version.toString(), description: schemaInfo.description, label: schemaInfo.label, references: schemaReferences, items: {} }; schemaInfo.references.forEach((ref) => { schemaReferences.push({ name: ref.schemaKey.name, version: ref.schemaKey.version.toString() }); }); return schemaProps; } async startLoadingPartialSchema(schemaProps, schemaContext) { if (schemaContext.schemaExists(SchemaKey_1.SchemaKey.parseString(`${schemaProps.name}.${schemaProps.version}`))) { return; } const controller = new SchemaLoadingController_1.SchemaLoadingController(); const schemaReader = new IncrementalSchemaReader_1.IncrementalSchemaReader(schemaContext, true); const schema = new Schema_1.Schema(schemaContext); schema.setLoadingController(controller); await schemaReader.readSchema(schema, schemaProps); if (!this._options.loadPartialSchemaOnly) controller.start(this.startLoadingFullSchema(schema)); } async loadFullSchema(schema) { const fullSchemaProps = await this.getSchemaJson(schema.schemaKey, schema.context); const reader = new IncrementalSchemaReader_1.IncrementalSchemaReader(schema.context, false); await reader.readSchema(schema, fullSchemaProps, false); } async startLoadingFullSchema(schema) { if (!schema.loadingController) return; // If the schema is already resolved, return it directly. if (schema.loadingController.isComplete || schema.loadingController.inProgress) { return; } // Since the schema relies on it's references, they get triggered to be resolved // first by recursively calling this method. After all references has been resolved // the schema itself gets resolved. await Promise.all(schema.references.map(async (referenceSchema) => { if (referenceSchema.loadingController && referenceSchema.loadingController.inProgress) return referenceSchema.loadingController.wait(); else return this.startLoadingFullSchema(referenceSchema); })); return this.loadFullSchema(schema); } async sortSchemaPartials(schemaPartials, schemaContext) { const schemaInfos = []; for (const schemaProps of schemaPartials) { const schemaKey = SchemaKey_1.SchemaKey.parseString(`${schemaProps.name}.${schemaProps.version}`); const schemaInfo = await schemaContext.getSchemaInfo(schemaKey, ECObjects_1.SchemaMatchType.Latest); if (!schemaInfo) throw new Error(`Schema ${schemaKey.name} could not be found.`); schemaInfos.push({ ...schemaInfo, props: schemaProps }); } const orderedSchemaInfos = SchemaGraphUtil_1.SchemaGraphUtil.buildDependencyOrderedSchemaInfoList(schemaInfos); return orderedSchemaInfos.map((schemaInfo) => schemaInfo.props); } } exports.IncrementalSchemaLocater = IncrementalSchemaLocater; /** * Helper class to manage schema infos for a schema context. */ class SchemaInfoCache { _schemaInfoCache; _schemaInfoLoader; constructor(schemaInfoLoader) { this._schemaInfoCache = new WeakMap(); this._schemaInfoLoader = schemaInfoLoader; } async getSchemasByContext(context) { if (!this._schemaInfoCache.has(context)) { const schemaInfos = await this._schemaInfoLoader(context); this._schemaInfoCache.set(context, Array.from(schemaInfos)); } return this._schemaInfoCache.get(context); } async lookup(schemaKey, matchType, context) { const contextSchemaInfos = await this.getSchemasByContext(context); return contextSchemaInfos ? contextSchemaInfos.find((schemaInfo) => schemaInfo.schemaKey.matches(schemaKey, matchType)) : undefined; } remove(schemaKey, context) { const contextSchemaInfos = this._schemaInfoCache.get(context); if (!contextSchemaInfos) return; const index = contextSchemaInfos.findIndex((schemaInfo) => schemaInfo.schemaKey.name === schemaKey.name); if (index !== -1) { contextSchemaInfos.splice(index, 1); } } } //# sourceMappingURL=IncrementalSchemaLocater.js.map