@itwin/presentation-core-interop 2.0.0-alpha.3 → 2.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +172 -0
- package/README.md +17 -7
- package/lib/core-interop/Formatting.d.ts +41 -12
- package/lib/core-interop/Formatting.d.ts.map +1 -1
- package/lib/core-interop/Formatting.js +49 -87
- package/lib/core-interop/Formatting.js.map +1 -1
- package/lib/core-interop/Metadata.d.ts +57 -11
- package/lib/core-interop/Metadata.d.ts.map +1 -1
- package/lib/core-interop/Metadata.js +480 -31
- package/lib/core-interop/Metadata.js.map +1 -1
- package/lib/core-interop/QueryExecutor.d.ts +1 -2
- package/lib/core-interop/QueryExecutor.d.ts.map +1 -1
- package/lib/core-interop/QueryExecutor.js.map +1 -1
- package/package.json +16 -16
- package/lib/core-interop/MetadataInternal.d.ts +0 -10
- package/lib/core-interop/MetadataInternal.d.ts.map +0 -1
- package/lib/core-interop/MetadataInternal.js +0 -424
- package/lib/core-interop/MetadataInternal.js.map +0 -1
|
@@ -1,16 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import { type EC, type ECSchemaProvider } from "@itwin/presentation-shared";
|
|
2
|
+
import type { SchemaView as CoreSchemaView } from "@itwin/ecschema-metadata";
|
|
3
|
+
import type { CoreECSqlReaderFactory } from "./QueryExecutor.js";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Subset of [SchemaView](https://www.itwinjs.org/reference/ecschema-metadata/context/schemaview/) API surface that
|
|
6
|
+
* is `@public` and can be used by `createECSchemaProvider` function.
|
|
7
|
+
*
|
|
7
8
|
* @public
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
type PublicCoreSchemaView = Pick<CoreSchemaView, "schemaToken" | "isOutdated" | "schemaCount" | "classCount" | "getSchema" | "getSchemaByAlias" | "getSchemas" | "findClass" | "findEnumeration" | "findKindOfQuantity" | "findPropertyCategory">;
|
|
11
|
+
/**
|
|
12
|
+
* A function that optionally takes a list of schema names and returns a `SchemaView`, containing details about the
|
|
13
|
+
* requested schemas.
|
|
14
|
+
*
|
|
15
|
+
* If no schema names are provided, the function should return a `SchemaView` containing all available schemas.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
type CoreSchemaViewGetter = (props?: {
|
|
20
|
+
schemas?: string[];
|
|
21
|
+
}) => Promise<PublicCoreSchemaView>;
|
|
22
|
+
/**
|
|
23
|
+
* Accumulates schema names requested within the same frame and issues a single `getSchemaView` request for all of
|
|
24
|
+
* them in the next one, returning each caller the batch's result. Used both by `createECSchemaProvider` and by
|
|
25
|
+
* `createValueFormatter` (which resolves kind of quantity persistence units without needing the class hierarchy).
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export declare function createBatchedSchemaViewGetter<TSchemaView>(imodel: {
|
|
30
|
+
getSchemaView: (props?: {
|
|
31
|
+
schemas?: string[];
|
|
32
|
+
}) => Promise<TSchemaView>;
|
|
33
|
+
}): (schemaName: string) => Promise<TSchemaView>;
|
|
12
34
|
/**
|
|
13
|
-
* Creates an `ECSchemaProvider` for given [
|
|
35
|
+
* Creates an `ECSchemaProvider` for a given iModel with [SchemaView](https://www.itwinjs.org/reference/ecschema-metadata/context/schemaview/)
|
|
36
|
+
* getter and query reader factory.
|
|
14
37
|
*
|
|
15
38
|
* Usage example:
|
|
16
39
|
*
|
|
@@ -19,12 +42,35 @@ interface CoreSchemaContext {
|
|
|
19
42
|
* import { createECSchemaProvider } from "@itwin/presentation-core-interop";
|
|
20
43
|
*
|
|
21
44
|
* const imodel: IModelConnection = getIModel();
|
|
22
|
-
* const schemaProvider = createECSchemaProvider(imodel
|
|
45
|
+
* const schemaProvider = createECSchemaProvider(imodel);
|
|
23
46
|
* // the created schema provider may be used in `@itwin/presentation-hierarchies` and other Presentation packages
|
|
24
47
|
* ```
|
|
25
48
|
*
|
|
26
49
|
* @public
|
|
27
50
|
*/
|
|
28
|
-
export declare function createECSchemaProvider(
|
|
51
|
+
export declare function createECSchemaProvider(imodel: {
|
|
52
|
+
getSchemaView: CoreSchemaViewGetter;
|
|
53
|
+
} & CoreECSqlReaderFactory): ECSchemaProvider;
|
|
54
|
+
interface SchemaViewProviderContext {
|
|
55
|
+
schemaView: PublicCoreSchemaView;
|
|
56
|
+
classHierarchyResolver: ECClassHierarchyResolver;
|
|
57
|
+
schema: EC.Schema;
|
|
58
|
+
/** Shared cache of built `EC.Class` objects keyed by full class name, to avoid rebuilding them on every access. */
|
|
59
|
+
classCache: Map<string, EC.Class>;
|
|
60
|
+
}
|
|
61
|
+
export declare function createECSchemaFromSchemaView(svSchema: CoreSchemaView.Schema, context: Omit<SchemaViewProviderContext, "schema">): EC.Schema;
|
|
62
|
+
export declare function createECClassFromSchemaView(svClass: CoreSchemaView.Class, context: SchemaViewProviderContext): EC.Class;
|
|
63
|
+
export declare function createECPropertyFromSchemaView(svProp: CoreSchemaView.Property, ecClass: EC.Class, context: SchemaViewProviderContext): EC.Property;
|
|
64
|
+
/** @internal */
|
|
65
|
+
export interface ECClassHierarchyResolver {
|
|
66
|
+
/** Check if the derived class derives from the candidate base class. */
|
|
67
|
+
classDerivesFrom(derivedClassFullName: EC.FullClassNameDotNotation, candidateBaseClassFullName: EC.FullClassNameDotNotation): boolean;
|
|
68
|
+
/** Get names of all derived classes of the specified class. */
|
|
69
|
+
getDerivedClassNames(classFullName: EC.FullClassNameDotNotation, options?: {
|
|
70
|
+
onlyDirect?: boolean;
|
|
71
|
+
}): EC.FullClassNameDotNotation[];
|
|
72
|
+
}
|
|
73
|
+
/** @internal */
|
|
74
|
+
export declare function createECClassHierarchyResolver(imodel: CoreECSqlReaderFactory): Promise<ECClassHierarchyResolver>;
|
|
29
75
|
export {};
|
|
30
76
|
//# sourceMappingURL=Metadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Metadata.d.ts","sourceRoot":"","sources":["../../src/core-interop/Metadata.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Metadata.d.ts","sourceRoot":"","sources":["../../src/core-interop/Metadata.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,gBAAgB,EAA0B,MAAM,4BAA4B,CAAC;AAEpG,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE;;;;;GAKG;AACH,KAAK,oBAAoB,GAAG,IAAI,CAC9B,cAAc,EACZ,aAAa,GACb,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,WAAW,GACX,kBAAkB,GAClB,YAAY,GACZ,WAAW,GACX,iBAAiB,GACjB,oBAAoB,GACpB,sBAAsB,CACzB,CAAC;AAEF;;;;;;;GAOG;AACH,KAAK,oBAAoB,GAAG,CAAC,KAAK,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAE9F;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE;IACjE,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACzE,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAqB/C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE;IAAE,aAAa,EAAE,oBAAoB,CAAA;CAAE,GAAG,sBAAsB,GACvE,gBAAgB,CA6ElB;AAED,UAAU,yBAAyB;IACjC,UAAU,EAAE,oBAAoB,CAAC;IACjC,sBAAsB,EAAE,wBAAwB,CAAC;IACjD,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;IAClB,mHAAmH;IACnH,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;CACnC;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,cAAc,CAAC,MAAM,EAC/B,OAAO,EAAE,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,GACjD,EAAE,CAAC,MAAM,CAwBX;AAkBD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,cAAc,CAAC,KAAK,EAC7B,OAAO,EAAE,yBAAyB,GACjC,EAAE,CAAC,KAAK,CA8EV;AAiDD,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,cAAc,CAAC,QAAQ,EAC/B,OAAO,EAAE,EAAE,CAAC,KAAK,EACjB,OAAO,EAAE,yBAAyB,GACjC,EAAE,CAAC,QAAQ,CAsIb;AAmED,gBAAgB;AAChB,MAAM,WAAW,wBAAwB;IACvC,wEAAwE;IACxE,gBAAgB,CACd,oBAAoB,EAAE,EAAE,CAAC,wBAAwB,EACjD,0BAA0B,EAAE,EAAE,CAAC,wBAAwB,GACtD,OAAO,CAAC;IACX,+DAA+D;IAC/D,oBAAoB,CAClB,aAAa,EAAE,EAAE,CAAC,wBAAwB,EAC1C,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACjC,EAAE,CAAC,wBAAwB,EAAE,CAAC;CAClC;AAED,gBAAgB;AAChB,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,wBAAwB,CAAC,CAgEnC"}
|
|
@@ -2,10 +2,31 @@
|
|
|
2
2
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { bufferTime, filter, firstValueFrom, map, mergeMap, share, Subject } from "rxjs";
|
|
6
|
+
import { SchemaViewPrimitiveType, StrengthDirection } from "@itwin/ecschema-metadata";
|
|
7
|
+
import { normalizeFullClassName } from "@itwin/presentation-shared";
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* Accumulates schema names requested within the same frame and issues a single `getSchemaView` request for all of
|
|
10
|
+
* them in the next one, returning each caller the batch's result. Used both by `createECSchemaProvider` and by
|
|
11
|
+
* `createValueFormatter` (which resolves kind of quantity persistence units without needing the class hierarchy).
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export function createBatchedSchemaViewGetter(imodel) {
|
|
16
|
+
const schemaNameSubject = new Subject();
|
|
17
|
+
const schemaViewBatches = schemaNameSubject.pipe(bufferTime(0), filter((schemaNames) => schemaNames.length > 0), mergeMap(async (schemaNames) => {
|
|
18
|
+
const schemas = new Set(schemaNames);
|
|
19
|
+
return { schemas, schemaView: await imodel.getSchemaView({ schemas: [...schemas] }) };
|
|
20
|
+
}), share());
|
|
21
|
+
return async function getSchemaView(schemaName) {
|
|
22
|
+
const schemaView = firstValueFrom(schemaViewBatches.pipe(filter((batch) => batch.schemas.has(schemaName)), map((batch) => batch.schemaView)));
|
|
23
|
+
schemaNameSubject.next(schemaName);
|
|
24
|
+
return schemaView;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates an `ECSchemaProvider` for a given iModel with [SchemaView](https://www.itwinjs.org/reference/ecschema-metadata/context/schemaview/)
|
|
29
|
+
* getter and query reader factory.
|
|
9
30
|
*
|
|
10
31
|
* Usage example:
|
|
11
32
|
*
|
|
@@ -14,46 +35,474 @@ import { createECSchema } from "./MetadataInternal.js";
|
|
|
14
35
|
* import { createECSchemaProvider } from "@itwin/presentation-core-interop";
|
|
15
36
|
*
|
|
16
37
|
* const imodel: IModelConnection = getIModel();
|
|
17
|
-
* const schemaProvider = createECSchemaProvider(imodel
|
|
38
|
+
* const schemaProvider = createECSchemaProvider(imodel);
|
|
18
39
|
* // the created schema provider may be used in `@itwin/presentation-hierarchies` and other Presentation packages
|
|
19
40
|
* ```
|
|
20
41
|
*
|
|
21
42
|
* @public
|
|
22
43
|
*/
|
|
23
|
-
export function createECSchemaProvider(
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
44
|
+
export function createECSchemaProvider(imodel) {
|
|
45
|
+
const getSchemaView = createBatchedSchemaViewGetter(imodel);
|
|
46
|
+
// Ensures we only create a single `ECClassHierarchyResolver` for the iModel, which is used to resolve derived classes for all schemas.
|
|
47
|
+
// Cache the promise (not the resolved value) so concurrent `getSchema` calls share one `createECClassHierarchyResolver` invocation.
|
|
48
|
+
let cachedClassHierarchyResolverPromise;
|
|
49
|
+
// Set once the resolver promise settles. Allows `classDerivesFrom` to answer synchronously after the hierarchy is loaded.
|
|
50
|
+
let cachedClassHierarchyResolver;
|
|
51
|
+
function getClassHierarchyResolver() {
|
|
52
|
+
if (cachedClassHierarchyResolver) {
|
|
53
|
+
return cachedClassHierarchyResolver;
|
|
33
54
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
cachedClassHierarchyResolverPromise ??= createECClassHierarchyResolver(imodel).then((resolver) => {
|
|
56
|
+
cachedClassHierarchyResolver = resolver;
|
|
57
|
+
return resolver;
|
|
58
|
+
});
|
|
59
|
+
return cachedClassHierarchyResolverPromise;
|
|
60
|
+
}
|
|
61
|
+
async function getSchemaProviderContext(schemaName) {
|
|
62
|
+
const [classHierarchyResolver, schemaView] = await Promise.all([
|
|
63
|
+
getClassHierarchyResolver(),
|
|
64
|
+
getSchemaView(schemaName),
|
|
65
|
+
]);
|
|
66
|
+
return { classHierarchyResolver, schemaView };
|
|
67
|
+
}
|
|
68
|
+
// Cache the resolved schema by name so repeated `getSchema`/`getClass` calls reuse it instead of issuing a new
|
|
69
|
+
// `getSchemaView` request each time. The cached promise doubles as an in-flight guard: concurrent
|
|
70
|
+
// first-time requests for the same schema share it (and its underlying `getSchemaView` request) instead of each
|
|
71
|
+
// building the schema. The entry is retained until the schema view it was built from is marked outdated by the host
|
|
72
|
+
// (a newer view has replaced it), at which point the next access refreshes it.
|
|
73
|
+
const schemaCache = new Map();
|
|
74
|
+
async function fetchSchema(name) {
|
|
75
|
+
// Cache built `EC.Class` objects (by full name) for this schema, so repeated accesses (e.g. the
|
|
76
|
+
// `EC.Property.class` getter, invoked once per grouped node) reuse a single class instead of rebuilding it from
|
|
77
|
+
// the schema view every time. Scoped to the fetch so a later refetch (when the view is outdated) starts fresh.
|
|
78
|
+
const classCache = new Map();
|
|
79
|
+
const entry = (async () => {
|
|
80
|
+
const { classHierarchyResolver, schemaView } = await getSchemaProviderContext(name);
|
|
81
|
+
const svSchema = schemaView.getSchema(name);
|
|
82
|
+
const schema = svSchema
|
|
83
|
+
? createECSchemaFromSchemaView(svSchema, { schemaView, classHierarchyResolver, classCache })
|
|
84
|
+
: undefined;
|
|
85
|
+
return { schemaView, schema };
|
|
86
|
+
})();
|
|
87
|
+
schemaCache.set(name, entry);
|
|
88
|
+
// Drop rejected entries so a transient failure doesn't get cached permanently.
|
|
89
|
+
/* v8 ignore next 4 -- defensive cleanup for rejected cache entries while a newer entry may have already replaced this one */
|
|
90
|
+
entry.catch(() => {
|
|
91
|
+
if (schemaCache.get(name) === entry) {
|
|
92
|
+
schemaCache.delete(name);
|
|
44
93
|
}
|
|
45
|
-
|
|
46
|
-
|
|
94
|
+
});
|
|
95
|
+
return entry;
|
|
47
96
|
}
|
|
48
97
|
return {
|
|
49
98
|
async getSchema(name) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
99
|
+
const cached = schemaCache.get(name);
|
|
100
|
+
const entry = await (cached ?? fetchSchema(name));
|
|
101
|
+
return entry.schemaView.isOutdated ? (await fetchSchema(name)).schema : entry.schema;
|
|
102
|
+
},
|
|
103
|
+
classDerivesFrom(derivedClassFullName, candidateBaseClassFullName) {
|
|
104
|
+
// A class always derives from itself. This matches the semantics of the deprecated `ECClassHierarchyInspector`.
|
|
105
|
+
if (derivedClassFullName === candidateBaseClassFullName) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
const resolver = getClassHierarchyResolver();
|
|
109
|
+
return resolver instanceof Promise
|
|
110
|
+
? resolver.then((r) => r.classDerivesFrom(derivedClassFullName, candidateBaseClassFullName))
|
|
111
|
+
: resolver.classDerivesFrom(derivedClassFullName, candidateBaseClassFullName);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function createECSchemaFromSchemaView(svSchema, context) {
|
|
116
|
+
const ecSchema = {
|
|
117
|
+
name: svSchema.name,
|
|
118
|
+
description: svSchema.description,
|
|
119
|
+
version: { read: svSchema.readVersion, write: svSchema.writeVersion, minor: svSchema.minorVersion },
|
|
120
|
+
isHidden: svSchema.isHidden,
|
|
121
|
+
getClass(name) {
|
|
122
|
+
const svClass = svSchema.getClass(name);
|
|
123
|
+
return svClass ? getECClassFromSchemaView(svClass, { ...context, schema: ecSchema }) : undefined;
|
|
124
|
+
},
|
|
125
|
+
getEnumeration(name) {
|
|
126
|
+
const svEnum = svSchema.getEnumeration(name);
|
|
127
|
+
return svEnum ? createECEnumerationFromSchemaView(svEnum, ecSchema) : undefined;
|
|
128
|
+
},
|
|
129
|
+
getKindOfQuantity(name) {
|
|
130
|
+
const svKoq = svSchema.getKindOfQuantity(name);
|
|
131
|
+
return svKoq ? createECKoqFromSchemaView(svKoq, ecSchema) : undefined;
|
|
132
|
+
},
|
|
133
|
+
getPropertyCategory(name) {
|
|
134
|
+
const svCategory = svSchema.getPropertyCategory(name);
|
|
135
|
+
return svCategory ? createECPropertyCategoryFromSchemaView(svCategory, ecSchema) : undefined;
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
return ecSchema;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Cache-aware entry point for building an `EC.Class` from a schema view class: returns a previously built class for
|
|
142
|
+
* the same full name, otherwise builds one via `createECClassFromSchemaView` and stores it in the context's cache.
|
|
143
|
+
*/
|
|
144
|
+
function getECClassFromSchemaView(svClass, context) {
|
|
145
|
+
// Key by the schema view's raw full name: it is a stable identifier for the class, so we avoid normalizing it on
|
|
146
|
+
// every (hot) cache hit. `createECClassFromSchemaView` normalizes once, on a miss, for the `EC.Class.fullName` field.
|
|
147
|
+
const cached = context.classCache.get(svClass.fullName);
|
|
148
|
+
if (cached) {
|
|
149
|
+
return cached;
|
|
150
|
+
}
|
|
151
|
+
const ecClass = createECClassFromSchemaView(svClass, context);
|
|
152
|
+
context.classCache.set(svClass.fullName, ecClass);
|
|
153
|
+
return ecClass;
|
|
154
|
+
}
|
|
155
|
+
export function createECClassFromSchemaView(svClass, context) {
|
|
156
|
+
const { classHierarchyResolver, schema } = context;
|
|
157
|
+
const fullName = normalizeFullClassName(svClass.fullName);
|
|
158
|
+
const ecClass = {
|
|
159
|
+
schema,
|
|
160
|
+
fullName,
|
|
161
|
+
name: svClass.name,
|
|
162
|
+
label: svClass.label,
|
|
163
|
+
description: svClass.description,
|
|
164
|
+
isHidden: svClass.isHidden,
|
|
165
|
+
isEntityClass() {
|
|
166
|
+
return svClass.isEntity();
|
|
167
|
+
},
|
|
168
|
+
isRelationshipClass() {
|
|
169
|
+
return svClass.isRelationship();
|
|
170
|
+
},
|
|
171
|
+
isStructClass() {
|
|
172
|
+
return svClass.isStruct();
|
|
173
|
+
},
|
|
174
|
+
isMixin() {
|
|
175
|
+
return svClass.isMixin();
|
|
176
|
+
},
|
|
177
|
+
get baseClass() {
|
|
178
|
+
return svClass.baseClass
|
|
179
|
+
? getECClassFromSchemaView(svClass.baseClass, {
|
|
180
|
+
...context,
|
|
181
|
+
schema: useOrCreateSchema(svClass.baseClass.schema, context),
|
|
182
|
+
})
|
|
183
|
+
: undefined;
|
|
184
|
+
},
|
|
185
|
+
is(classOrClassName, schemaName) {
|
|
186
|
+
if (typeof classOrClassName === "string") {
|
|
187
|
+
return svClass.is(`${schemaName}.${classOrClassName}`);
|
|
54
188
|
}
|
|
55
|
-
return
|
|
189
|
+
return svClass.is(classOrClassName.fullName);
|
|
190
|
+
},
|
|
191
|
+
getProperty(name) {
|
|
192
|
+
const svProp = svClass.getProperty(name);
|
|
193
|
+
return svProp ? createECPropertyFromSchemaView(svProp, ecClass, context) : undefined;
|
|
194
|
+
},
|
|
195
|
+
getProperties() {
|
|
196
|
+
return svClass.getProperties().map((p) => createECPropertyFromSchemaView(p, ecClass, context));
|
|
56
197
|
},
|
|
198
|
+
getOwnProperties() {
|
|
199
|
+
return svClass.getOwnProperties().map((p) => createECPropertyFromSchemaView(p, ecClass, context));
|
|
200
|
+
},
|
|
201
|
+
getDerivedClassNames(props) {
|
|
202
|
+
return classHierarchyResolver.getDerivedClassNames(fullName, props);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
if (svClass.isEntity()) {
|
|
206
|
+
const ecEntity = {
|
|
207
|
+
...ecClass,
|
|
208
|
+
getMixins() {
|
|
209
|
+
return svClass.mixins.map((m) => getECClassFromSchemaView(m, { ...context, schema: useOrCreateSchema(m.schema, context) }));
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
return ecEntity;
|
|
213
|
+
}
|
|
214
|
+
if (svClass.isRelationship()) {
|
|
215
|
+
const ecRel = {
|
|
216
|
+
...ecClass,
|
|
217
|
+
direction: svClass.strengthDirection === StrengthDirection.Forward ? "Forward" : "Backward",
|
|
218
|
+
source: svClass.source
|
|
219
|
+
? createECRelConstraintFromSchemaView(svClass.source, context)
|
|
220
|
+
: createEmptyRelConstraint(),
|
|
221
|
+
target: svClass.target
|
|
222
|
+
? createECRelConstraintFromSchemaView(svClass.target, context)
|
|
223
|
+
: createEmptyRelConstraint(),
|
|
224
|
+
};
|
|
225
|
+
return ecRel;
|
|
226
|
+
}
|
|
227
|
+
return ecClass;
|
|
228
|
+
}
|
|
229
|
+
function useOrCreateSchema(svSchema, context) {
|
|
230
|
+
const { schema: currentSchema, ...schemalessContext } = context;
|
|
231
|
+
if (svSchema.name === currentSchema.name) {
|
|
232
|
+
return currentSchema;
|
|
233
|
+
}
|
|
234
|
+
return createECSchemaFromSchemaView(svSchema, schemalessContext);
|
|
235
|
+
}
|
|
236
|
+
function createEmptyRelConstraint() {
|
|
237
|
+
return {
|
|
238
|
+
multiplicity: { lowerLimit: 0, upperLimit: 0 },
|
|
239
|
+
polymorphic: false,
|
|
240
|
+
constraintClasses: [],
|
|
241
|
+
abstractConstraint: undefined,
|
|
57
242
|
};
|
|
58
243
|
}
|
|
244
|
+
function createECRelConstraintFromSchemaView(svConstraint, context) {
|
|
245
|
+
return {
|
|
246
|
+
multiplicity: {
|
|
247
|
+
lowerLimit: svConstraint.multiplicityLower,
|
|
248
|
+
// `SchemaView` reports an unbounded upper limit as `0`.
|
|
249
|
+
upperLimit: svConstraint.multiplicityUpper === 0 ? "unbounded" : svConstraint.multiplicityUpper,
|
|
250
|
+
},
|
|
251
|
+
polymorphic: svConstraint.polymorphic,
|
|
252
|
+
get constraintClasses() {
|
|
253
|
+
return svConstraint.constraintClasses.map((c) => getECClassFromSchemaView(c, { ...context, schema: useOrCreateSchema(c.schema, context) }));
|
|
254
|
+
},
|
|
255
|
+
get abstractConstraint() {
|
|
256
|
+
// `SchemaView` only exposes an explicitly-defined abstract constraint. To match EC semantics (and the
|
|
257
|
+
// standard `RelationshipConstraint` implementation), fall back to the sole constraint class when there's exactly one.
|
|
258
|
+
const svClass = svConstraint.abstractConstraint ??
|
|
259
|
+
(svConstraint.constraintClasses.length === 1 ? svConstraint.constraintClasses[0] : undefined);
|
|
260
|
+
if (!svClass) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
return getECClassFromSchemaView(svClass, { ...context, schema: useOrCreateSchema(svClass.schema, context) });
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
export function createECPropertyFromSchemaView(svProp, ecClass, context) {
|
|
268
|
+
const base = {
|
|
269
|
+
// `svProp.declaringClass` is the class that declared or contributed this property (a base class for an
|
|
270
|
+
// inherited property, or a mixin for a mixin-contributed one). It's `undefined` only for view properties,
|
|
271
|
+
// in which case we fall back to the class the property was enumerated from.
|
|
272
|
+
get class() {
|
|
273
|
+
return svProp.declaringClass
|
|
274
|
+
? getECClassFromSchemaView(svProp.declaringClass, {
|
|
275
|
+
...context,
|
|
276
|
+
schema: useOrCreateSchema(svProp.declaringClass.schema, context),
|
|
277
|
+
})
|
|
278
|
+
: ecClass;
|
|
279
|
+
},
|
|
280
|
+
name: svProp.name,
|
|
281
|
+
description: svProp.description,
|
|
282
|
+
label: svProp.label,
|
|
283
|
+
isHidden: svProp.isHidden,
|
|
284
|
+
get category() {
|
|
285
|
+
return svProp.category
|
|
286
|
+
? createECPropertyCategoryFromSchemaView(svProp.category, useOrCreateSchema(svProp.category.schema, context))
|
|
287
|
+
: undefined;
|
|
288
|
+
},
|
|
289
|
+
isArray() {
|
|
290
|
+
return svProp.isArray();
|
|
291
|
+
},
|
|
292
|
+
isStruct() {
|
|
293
|
+
return false;
|
|
294
|
+
},
|
|
295
|
+
isPrimitive() {
|
|
296
|
+
return false;
|
|
297
|
+
},
|
|
298
|
+
isEnumeration() {
|
|
299
|
+
return false;
|
|
300
|
+
},
|
|
301
|
+
isNavigation() {
|
|
302
|
+
return false;
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
if (svProp.isNavigation()) {
|
|
306
|
+
return {
|
|
307
|
+
...base,
|
|
308
|
+
isNavigation() {
|
|
309
|
+
return true;
|
|
310
|
+
},
|
|
311
|
+
get direction() {
|
|
312
|
+
return svProp.direction === StrengthDirection.Forward ? "Forward" : "Backward";
|
|
313
|
+
},
|
|
314
|
+
get relationshipClass() {
|
|
315
|
+
return getECClassFromSchemaView(svProp.relationshipClass, {
|
|
316
|
+
...context,
|
|
317
|
+
schema: useOrCreateSchema(svProp.relationshipClass.schema, context),
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
// Check enumeration before primitive (enum is a facet of primitive in CoreSchemaView, but separate in EC)
|
|
323
|
+
if (svProp.isEnumeration()) {
|
|
324
|
+
const arrayFields = svProp.isArray()
|
|
325
|
+
? { minOccurs: svProp.arrayMinOccurs ?? 0, maxOccurs: svProp.arrayMaxOccurs }
|
|
326
|
+
: undefined;
|
|
327
|
+
return {
|
|
328
|
+
...base,
|
|
329
|
+
...arrayFields,
|
|
330
|
+
isEnumeration() {
|
|
331
|
+
return true;
|
|
332
|
+
},
|
|
333
|
+
isArray() {
|
|
334
|
+
return svProp.isArray();
|
|
335
|
+
},
|
|
336
|
+
get extendedTypeName() {
|
|
337
|
+
return svProp.extendedTypeName;
|
|
338
|
+
},
|
|
339
|
+
get enumeration() {
|
|
340
|
+
return svProp.enumeration
|
|
341
|
+
? createECEnumerationFromSchemaView(svProp.enumeration, useOrCreateSchema(svProp.enumeration.schema, context))
|
|
342
|
+
: undefined;
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
if (svProp.isPrimitive()) {
|
|
347
|
+
const arrayFields = svProp.isArray()
|
|
348
|
+
? { minOccurs: svProp.arrayMinOccurs ?? 0, maxOccurs: svProp.arrayMaxOccurs }
|
|
349
|
+
: undefined;
|
|
350
|
+
return {
|
|
351
|
+
...base,
|
|
352
|
+
...arrayFields,
|
|
353
|
+
isPrimitive() {
|
|
354
|
+
return true;
|
|
355
|
+
},
|
|
356
|
+
isArray() {
|
|
357
|
+
return svProp.isArray();
|
|
358
|
+
},
|
|
359
|
+
get primitiveType() {
|
|
360
|
+
return mapSchemaViewPrimitiveType(svProp.primitiveType);
|
|
361
|
+
},
|
|
362
|
+
get extendedTypeName() {
|
|
363
|
+
return svProp.extendedTypeName;
|
|
364
|
+
},
|
|
365
|
+
get kindOfQuantity() {
|
|
366
|
+
return svProp.kindOfQuantity
|
|
367
|
+
? createECKoqFromSchemaView(svProp.kindOfQuantity, useOrCreateSchema(svProp.kindOfQuantity.schema, context))
|
|
368
|
+
: undefined;
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (svProp.isStruct()) {
|
|
373
|
+
const arrayFields = svProp.isArray()
|
|
374
|
+
? { minOccurs: svProp.arrayMinOccurs ?? 0, maxOccurs: svProp.arrayMaxOccurs }
|
|
375
|
+
: undefined;
|
|
376
|
+
return {
|
|
377
|
+
...base,
|
|
378
|
+
...arrayFields,
|
|
379
|
+
isStruct() {
|
|
380
|
+
return true;
|
|
381
|
+
},
|
|
382
|
+
isArray() {
|
|
383
|
+
return svProp.isArray();
|
|
384
|
+
},
|
|
385
|
+
get structClass() {
|
|
386
|
+
return getECClassFromSchemaView(svProp.structClass, {
|
|
387
|
+
...context,
|
|
388
|
+
schema: useOrCreateSchema(svProp.structClass.schema, context),
|
|
389
|
+
});
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
throw new Error(`Unexpected property type for ${svProp.declaringClass ? svProp.declaringClass.fullName : "<ECCView>"}.${svProp.name}`);
|
|
394
|
+
}
|
|
395
|
+
function mapSchemaViewPrimitiveType(svType) {
|
|
396
|
+
switch (svType) {
|
|
397
|
+
case SchemaViewPrimitiveType.Binary:
|
|
398
|
+
return "Binary";
|
|
399
|
+
case SchemaViewPrimitiveType.Boolean:
|
|
400
|
+
return "Boolean";
|
|
401
|
+
case SchemaViewPrimitiveType.DateTime:
|
|
402
|
+
return "DateTime";
|
|
403
|
+
case SchemaViewPrimitiveType.Double:
|
|
404
|
+
return "Double";
|
|
405
|
+
case SchemaViewPrimitiveType.IGeometry:
|
|
406
|
+
return "IGeometry";
|
|
407
|
+
case SchemaViewPrimitiveType.Integer:
|
|
408
|
+
return "Integer";
|
|
409
|
+
case SchemaViewPrimitiveType.Long:
|
|
410
|
+
return "Long";
|
|
411
|
+
case SchemaViewPrimitiveType.Point2d:
|
|
412
|
+
return "Point2d";
|
|
413
|
+
case SchemaViewPrimitiveType.Point3d:
|
|
414
|
+
return "Point3d";
|
|
415
|
+
case SchemaViewPrimitiveType.String:
|
|
416
|
+
return "String";
|
|
417
|
+
}
|
|
418
|
+
throw new Error(`Uninitialized CoreSchemaView primitive type: ${svType}`);
|
|
419
|
+
}
|
|
420
|
+
function createECEnumerationFromSchemaView(svEnum, schema) {
|
|
421
|
+
return {
|
|
422
|
+
schema,
|
|
423
|
+
fullName: normalizeFullClassName(svEnum.fullName),
|
|
424
|
+
name: svEnum.name,
|
|
425
|
+
label: svEnum.label,
|
|
426
|
+
description: svEnum.description,
|
|
427
|
+
type: svEnum.primitiveType === SchemaViewPrimitiveType.Integer ? "Number" : "String",
|
|
428
|
+
isStrict: svEnum.isStrict,
|
|
429
|
+
enumerators: [...svEnum.getEnumerators()].map((e) => ({ name: e.name, label: e.label, value: e.value })),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function createECKoqFromSchemaView(svKoq, schema) {
|
|
433
|
+
return {
|
|
434
|
+
schema,
|
|
435
|
+
fullName: normalizeFullClassName(svKoq.fullName),
|
|
436
|
+
name: svKoq.name,
|
|
437
|
+
label: svKoq.label,
|
|
438
|
+
description: svKoq.description,
|
|
439
|
+
relativeError: svKoq.relativeError,
|
|
440
|
+
persistenceUnit: svKoq.persistenceUnit,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function createECPropertyCategoryFromSchemaView(svCategory, schema) {
|
|
444
|
+
return {
|
|
445
|
+
schema,
|
|
446
|
+
fullName: normalizeFullClassName(svCategory.fullName),
|
|
447
|
+
name: svCategory.name,
|
|
448
|
+
label: svCategory.label,
|
|
449
|
+
description: svCategory.description,
|
|
450
|
+
priority: svCategory.priority,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/** @internal */
|
|
454
|
+
export async function createECClassHierarchyResolver(imodel) {
|
|
455
|
+
const baseToDerivedMap = new Map();
|
|
456
|
+
const derivedToBaseMap = new Map();
|
|
457
|
+
const ecsql = `
|
|
458
|
+
SELECT
|
|
459
|
+
ec_classname(rel.SourceECInstanceId, 's.c'),
|
|
460
|
+
ec_classname(rel.TargetECInstanceId, 's.c')
|
|
461
|
+
FROM meta.ClassHasBaseClasses rel
|
|
462
|
+
`;
|
|
463
|
+
const reader = imodel.createQueryReader(ecsql);
|
|
464
|
+
for await (const row of reader) {
|
|
465
|
+
const derivedClass = row[0];
|
|
466
|
+
const baseClass = row[1];
|
|
467
|
+
let baseToDerivedEntry = baseToDerivedMap.get(baseClass);
|
|
468
|
+
if (!baseToDerivedEntry) {
|
|
469
|
+
baseToDerivedEntry = new Set();
|
|
470
|
+
baseToDerivedMap.set(baseClass, baseToDerivedEntry);
|
|
471
|
+
}
|
|
472
|
+
baseToDerivedEntry.add(derivedClass);
|
|
473
|
+
let derivedToBaseEntry = derivedToBaseMap.get(derivedClass);
|
|
474
|
+
if (!derivedToBaseEntry) {
|
|
475
|
+
derivedToBaseEntry = new Set();
|
|
476
|
+
derivedToBaseMap.set(derivedClass, derivedToBaseEntry);
|
|
477
|
+
}
|
|
478
|
+
derivedToBaseEntry.add(baseClass);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* TODO: We don't really need this in schema provider, but maybe this function should become part of it? Would allow us to drop
|
|
482
|
+
* ECClassHierarchyInspector, whose implementation currently has to load schemas (this one doesn't).
|
|
483
|
+
*/
|
|
484
|
+
function classDerivesFrom(derivedClassFullName, candidateBaseClassFullName) {
|
|
485
|
+
const baseClasses = derivedToBaseMap.get(derivedClassFullName);
|
|
486
|
+
return baseClasses
|
|
487
|
+
? baseClasses.has(candidateBaseClassFullName) ||
|
|
488
|
+
baseClasses.values().some((baseClass) => classDerivesFrom(baseClass, candidateBaseClassFullName))
|
|
489
|
+
: false;
|
|
490
|
+
}
|
|
491
|
+
function getDerivedClassNames(classFullName, options) {
|
|
492
|
+
const derivedClasses = baseToDerivedMap.get(classFullName);
|
|
493
|
+
if (!derivedClasses) {
|
|
494
|
+
return [];
|
|
495
|
+
}
|
|
496
|
+
if (options?.onlyDirect) {
|
|
497
|
+
return [...derivedClasses];
|
|
498
|
+
}
|
|
499
|
+
const allDerivedClasses = new Set();
|
|
500
|
+
for (const derivedClass of derivedClasses) {
|
|
501
|
+
allDerivedClasses.add(derivedClass);
|
|
502
|
+
getDerivedClassNames(derivedClass, options).forEach((subDerivedClass) => allDerivedClasses.add(subDerivedClass));
|
|
503
|
+
}
|
|
504
|
+
return Array.from(allDerivedClasses);
|
|
505
|
+
}
|
|
506
|
+
return { classDerivesFrom, getDerivedClassNames };
|
|
507
|
+
}
|
|
59
508
|
//# sourceMappingURL=Metadata.js.map
|