@itwin/imodel-transformer 0.4.4-dev.0 → 0.4.18-dev.0
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 +1 -9
- package/lib/cjs/Algo.d.ts +7 -0
- package/lib/cjs/Algo.d.ts.map +1 -0
- package/lib/cjs/Algo.js +65 -0
- package/lib/cjs/Algo.js.map +1 -0
- package/lib/cjs/BigMap.d.ts +1 -6
- package/lib/cjs/BigMap.d.ts.map +1 -1
- package/lib/cjs/BigMap.js +3 -29
- package/lib/cjs/BigMap.js.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.d.ts +10 -3
- package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +101 -10
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +12 -5
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +32 -18
- package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js +7 -5
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.d.ts +3 -3
- package/lib/cjs/ElementCascadingDeleter.d.ts.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.js +9 -7
- package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
- package/lib/cjs/EntityMap.d.ts.map +1 -1
- package/lib/cjs/EntityMap.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +5 -0
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +22 -35
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +5 -4
- package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +9 -5
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +23 -14
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +87 -21
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +279 -114
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +29 -21
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +115 -62
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +285 -48
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +1273 -337
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/PendingReferenceMap.d.ts.map +1 -1
- package/lib/cjs/PendingReferenceMap.js +12 -6
- package/lib/cjs/PendingReferenceMap.js.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.d.ts +2 -2
- package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +5 -5
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/transformer.d.ts.map +1 -1
- package/lib/cjs/transformer.js +14 -10
- package/lib/cjs/transformer.js.map +1 -1
- package/package.json +20 -17
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
6
|
/** @packageDocumentation
|
|
7
7
|
* @module iModels
|
|
8
8
|
*/
|
|
@@ -13,6 +13,7 @@ const core_bentley_1 = require("@itwin/core-bentley");
|
|
|
13
13
|
const core_common_1 = require("@itwin/core-common");
|
|
14
14
|
const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
|
|
15
15
|
const TransformerLoggerCategory_1 = require("./TransformerLoggerCategory");
|
|
16
|
+
const nodeAssert = require("assert");
|
|
16
17
|
const ExportElementAspectsWithElementsStrategy_1 = require("./ExportElementAspectsWithElementsStrategy");
|
|
17
18
|
const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelExporter;
|
|
18
19
|
/** Handles the events generated by IModelExporter.
|
|
@@ -25,7 +26,9 @@ class IModelExportHandler {
|
|
|
25
26
|
/** If `true` is returned, then the CodeSpec will be exported.
|
|
26
27
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
27
28
|
*/
|
|
28
|
-
shouldExportCodeSpec(_codeSpec) {
|
|
29
|
+
shouldExportCodeSpec(_codeSpec) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
29
32
|
/** Called when a CodeSpec should be exported.
|
|
30
33
|
* @param codeSpec The CodeSpec to export
|
|
31
34
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
@@ -49,7 +52,9 @@ class IModelExportHandler {
|
|
|
49
52
|
/** If `true` is returned, then the element will be exported.
|
|
50
53
|
* @note This method can optionally be overridden to exclude an individual Element (and its children and ElementAspects) from the export. The base implementation always returns `true`.
|
|
51
54
|
*/
|
|
52
|
-
shouldExportElement(_element) {
|
|
55
|
+
shouldExportElement(_element) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
53
58
|
/** Called when element is skipped instead of exported. */
|
|
54
59
|
onSkipElement(_elementId) { }
|
|
55
60
|
/** Called when an element should be exported.
|
|
@@ -70,7 +75,9 @@ class IModelExportHandler {
|
|
|
70
75
|
/** If `true` is returned, then the ElementAspect will be exported.
|
|
71
76
|
* @note This method can optionally be overridden to exclude an individual ElementAspect from the export. The base implementation always returns `true`.
|
|
72
77
|
*/
|
|
73
|
-
shouldExportElementAspect(_aspect) {
|
|
78
|
+
shouldExportElementAspect(_aspect) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
74
81
|
/** Called when an ElementUniqueAspect should be exported.
|
|
75
82
|
* @param aspect The ElementUniqueAspect to export
|
|
76
83
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
@@ -84,7 +91,9 @@ class IModelExportHandler {
|
|
|
84
91
|
/** If `true` is returned, then the relationship will be exported.
|
|
85
92
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
86
93
|
*/
|
|
87
|
-
shouldExportRelationship(_relationship) {
|
|
94
|
+
shouldExportRelationship(_relationship) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
88
97
|
/** Called when a Relationship should be exported.
|
|
89
98
|
* @param relationship The Relationship to export
|
|
90
99
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
@@ -96,7 +105,9 @@ class IModelExportHandler {
|
|
|
96
105
|
/** If `true` is returned, then the schema will be exported.
|
|
97
106
|
* @note This method can optionally be overridden to exclude an individual schema from the export. The base implementation always returns `true`.
|
|
98
107
|
*/
|
|
99
|
-
shouldExportSchema(_schemaKey) {
|
|
108
|
+
shouldExportSchema(_schemaKey) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
100
111
|
/** Called when a schema should be exported.
|
|
101
112
|
* @param schema The schema to export
|
|
102
113
|
* @note This should be overridden to actually do the export.
|
|
@@ -120,7 +131,7 @@ class IModelExporter {
|
|
|
120
131
|
/**
|
|
121
132
|
* Retrieve the cached entity change information.
|
|
122
133
|
* @note This will only be initialized after [IModelExporter.exportChanges] is invoked.
|
|
123
|
-
|
|
134
|
+
*/
|
|
124
135
|
get sourceDbChanges() {
|
|
125
136
|
return this._sourceDbChanges;
|
|
126
137
|
}
|
|
@@ -175,6 +186,7 @@ class IModelExporter {
|
|
|
175
186
|
this._excludedElementClasses = new Set();
|
|
176
187
|
/** The set of classes of Relationships that will be excluded (polymorphically) from transformation to the target iModel. */
|
|
177
188
|
this._excludedRelationshipClasses = new Set();
|
|
189
|
+
this._resetChangeDataOnExport = true;
|
|
178
190
|
this._yieldManager = new core_bentley_1.YieldManager();
|
|
179
191
|
this.sourceDb = sourceDb;
|
|
180
192
|
this._exportElementAspectsStrategy = new elementAspectsStrategy(this.sourceDb, {
|
|
@@ -184,6 +196,24 @@ class IModelExporter {
|
|
|
184
196
|
trackProgress: async () => this.trackProgress(),
|
|
185
197
|
});
|
|
186
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Initialize prerequisites of exporting. This is implicitly done by any `export*` calls that need initialization
|
|
201
|
+
* which is currently just `exportChanges`.
|
|
202
|
+
* Prefer to not call this explicitly (e.g. just call [[IModelExporter.exportChanges]])
|
|
203
|
+
* @note that if you do call this explicitly, you must do so with the same options that
|
|
204
|
+
* you pass to [[IModelExporter.exportChanges]]
|
|
205
|
+
*/
|
|
206
|
+
async initialize(options) {
|
|
207
|
+
if (!this.sourceDb.isBriefcaseDb() || this._sourceDbChanges)
|
|
208
|
+
return;
|
|
209
|
+
this._sourceDbChanges = await ChangedInstanceIds.initialize({
|
|
210
|
+
iModel: this.sourceDb,
|
|
211
|
+
...options,
|
|
212
|
+
});
|
|
213
|
+
if (this._sourceDbChanges === undefined)
|
|
214
|
+
return;
|
|
215
|
+
this._exportElementAspectsStrategy.setAspectChanges(this._sourceDbChanges.aspect);
|
|
216
|
+
}
|
|
187
217
|
/** Register the handler that will be called by IModelExporter. */
|
|
188
218
|
registerHandler(handler) {
|
|
189
219
|
this._handler = handler;
|
|
@@ -216,40 +246,38 @@ class IModelExporter {
|
|
|
216
246
|
* @note [[exportSchemas]] must be called separately.
|
|
217
247
|
*/
|
|
218
248
|
async exportAll() {
|
|
249
|
+
await this.initialize({});
|
|
219
250
|
await this.exportCodeSpecs();
|
|
220
251
|
await this.exportFonts();
|
|
221
252
|
await this.exportModel(core_common_1.IModel.repositoryModelId);
|
|
222
253
|
await this.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
|
|
223
254
|
}
|
|
224
|
-
async exportChanges(
|
|
255
|
+
async exportChanges(accessTokenOrOpts, startChangesetId) {
|
|
225
256
|
if (!this.sourceDb.isBriefcaseDb())
|
|
226
257
|
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Must be a briefcase to export changes");
|
|
227
258
|
if ("" === this.sourceDb.changeset.id) {
|
|
228
259
|
await this.exportAll(); // no changesets, so revert to exportAll
|
|
229
260
|
return;
|
|
230
261
|
}
|
|
231
|
-
const
|
|
232
|
-
?
|
|
262
|
+
const initOpts = typeof accessTokenOrOpts === "object"
|
|
263
|
+
? accessTokenOrOpts
|
|
233
264
|
: {
|
|
234
|
-
accessToken:
|
|
265
|
+
accessToken: accessTokenOrOpts,
|
|
235
266
|
startChangeset: { id: startChangesetId },
|
|
236
267
|
};
|
|
237
|
-
this.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
: undefined)
|
|
241
|
-
?? await ChangedInstanceIds.initialize({
|
|
242
|
-
accessToken,
|
|
243
|
-
startChangeset: startChangeset?.id !== undefined || startChangeset?.index !== undefined
|
|
244
|
-
? startChangeset
|
|
245
|
-
: this.sourceDb.changeset,
|
|
246
|
-
iModel: this.sourceDb,
|
|
247
|
-
});
|
|
248
|
-
this._exportElementAspectsStrategy.setAspectChanges(this._sourceDbChanges?.aspect);
|
|
268
|
+
await this.initialize(initOpts);
|
|
269
|
+
// _sourceDbChanges are initialized in this.initialize
|
|
270
|
+
nodeAssert(this._sourceDbChanges !== undefined, "sourceDbChanges must be initialized.");
|
|
249
271
|
await this.exportCodeSpecs();
|
|
250
272
|
await this.exportFonts();
|
|
251
|
-
|
|
252
|
-
|
|
273
|
+
if (initOpts.skipPropagateChangesToRootElements) {
|
|
274
|
+
await this.exportModelContents(core_common_1.IModel.repositoryModelId);
|
|
275
|
+
await this.exportSubModels(core_common_1.IModel.repositoryModelId);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
await this.exportModel(core_common_1.IModel.repositoryModelId);
|
|
279
|
+
}
|
|
280
|
+
await this.exportAllAspects();
|
|
253
281
|
await this.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
|
|
254
282
|
// handle deletes
|
|
255
283
|
if (this.visitElements) {
|
|
@@ -268,17 +296,25 @@ class IModelExporter {
|
|
|
268
296
|
this.handler.onDeleteElement(elementId);
|
|
269
297
|
}
|
|
270
298
|
catch (err) {
|
|
271
|
-
const isMissingErr = err instanceof core_common_1.IModelError &&
|
|
299
|
+
const isMissingErr = err instanceof core_common_1.IModelError &&
|
|
300
|
+
err.errorNumber === core_bentley_1.IModelStatus.NotFound;
|
|
272
301
|
if (!isMissingErr)
|
|
273
302
|
throw err;
|
|
274
303
|
}
|
|
275
304
|
}
|
|
276
305
|
}
|
|
277
306
|
if (this.visitRelationships) {
|
|
278
|
-
for (const relInstanceId of this._sourceDbChanges.relationship
|
|
307
|
+
for (const relInstanceId of this._sourceDbChanges.relationship
|
|
308
|
+
.deleteIds) {
|
|
279
309
|
this.handler.onDeleteRelationship(relInstanceId);
|
|
280
310
|
}
|
|
281
311
|
}
|
|
312
|
+
// Enable consecutive exportChanges runs without the need to re-instantiate the exporter.
|
|
313
|
+
// You can counteract the obvious impact of losing this expensive data by always calling
|
|
314
|
+
// exportChanges with the [[ExportChangesOptions.changedInstanceIds]] option set to
|
|
315
|
+
// whatever you want
|
|
316
|
+
if (this._resetChangeDataOnExport)
|
|
317
|
+
this._sourceDbChanges = undefined;
|
|
282
318
|
}
|
|
283
319
|
/** Export schemas from the source iModel.
|
|
284
320
|
* @note This must be called separately from [[exportAll]] or [[exportChanges]].
|
|
@@ -288,7 +324,9 @@ class IModelExporter {
|
|
|
288
324
|
const sql = `
|
|
289
325
|
SELECT s.Name, s.VersionMajor, s.VersionWrite, s.VersionMinor
|
|
290
326
|
FROM ECDbMeta.ECSchemaDef s
|
|
291
|
-
${this.wantSystemSchemas
|
|
327
|
+
${this.wantSystemSchemas
|
|
328
|
+
? ""
|
|
329
|
+
: `
|
|
292
330
|
WHERE ECInstanceId >= (SELECT ECInstanceId FROM ECDbMeta.ECSchemaDef WHERE Name='BisCore')
|
|
293
331
|
`}
|
|
294
332
|
ORDER BY ECInstanceId
|
|
@@ -324,8 +362,8 @@ class IModelExporter {
|
|
|
324
362
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
325
363
|
*/
|
|
326
364
|
async exportCodeSpecs() {
|
|
327
|
-
core_bentley_1.Logger.logTrace(loggerCategory,
|
|
328
|
-
const sql =
|
|
365
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "exportCodeSpecs()");
|
|
366
|
+
const sql = "SELECT Name FROM BisCore:CodeSpec ORDER BY ECInstanceId";
|
|
329
367
|
await this.sourceDb.withPreparedStatement(sql, async (statement) => {
|
|
330
368
|
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
331
369
|
const codeSpecName = statement.getValue(0).getString();
|
|
@@ -339,7 +377,8 @@ class IModelExporter {
|
|
|
339
377
|
async exportCodeSpecByName(codeSpecName) {
|
|
340
378
|
const codeSpec = this.sourceDb.codeSpecs.getByName(codeSpecName);
|
|
341
379
|
let isUpdate;
|
|
342
|
-
if (undefined !== this._sourceDbChanges) {
|
|
380
|
+
if (undefined !== this._sourceDbChanges) {
|
|
381
|
+
// is changeset information available?
|
|
343
382
|
if (this._sourceDbChanges.codeSpec.insertIds.has(codeSpec.id)) {
|
|
344
383
|
isUpdate = false;
|
|
345
384
|
}
|
|
@@ -373,7 +412,7 @@ class IModelExporter {
|
|
|
373
412
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
374
413
|
*/
|
|
375
414
|
async exportFonts() {
|
|
376
|
-
core_bentley_1.Logger.logTrace(loggerCategory,
|
|
415
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "exportFonts()");
|
|
377
416
|
for (const font of this.sourceDb.fontMap.fonts.values()) {
|
|
378
417
|
await this.exportFontByNumber(font.id);
|
|
379
418
|
}
|
|
@@ -392,19 +431,12 @@ class IModelExporter {
|
|
|
392
431
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
393
432
|
*/
|
|
394
433
|
async exportFontByNumber(fontNumber) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
else if (this._sourceDbChanges.font.updateIds.has(fontId)) {
|
|
402
|
-
isUpdate = true;
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
return; // not in changeset, don't export
|
|
406
|
-
}
|
|
407
|
-
}
|
|
434
|
+
/** sourceDbChanges now works by using TS ChangesetECAdaptor which doesn't pick up changes to fonts since fonts is not an ec table.
|
|
435
|
+
* So lets always export fonts for the time being by always setting isUpdate = true.
|
|
436
|
+
* It is very rare and even problematic for the font table to reach a large size, so it is not a bottleneck in transforming changes.
|
|
437
|
+
* See https://github.com/iTwin/imodel-transformer/pull/135 for removed code.
|
|
438
|
+
*/
|
|
439
|
+
const isUpdate = true;
|
|
408
440
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontById(${fontNumber})`);
|
|
409
441
|
const font = this.sourceDb.fontMap.getFont(fontNumber);
|
|
410
442
|
if (undefined !== font) {
|
|
@@ -420,7 +452,11 @@ class IModelExporter {
|
|
|
420
452
|
if (model.isTemplate && !this.wantTemplateModels) {
|
|
421
453
|
return;
|
|
422
454
|
}
|
|
423
|
-
const modeledElement = this.sourceDb.elements.getElement({
|
|
455
|
+
const modeledElement = this.sourceDb.elements.getElement({
|
|
456
|
+
id: modeledElementId,
|
|
457
|
+
wantGeometry: this.wantGeometry,
|
|
458
|
+
wantBRepData: this.wantGeometry,
|
|
459
|
+
});
|
|
424
460
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportModel(${modeledElementId})`);
|
|
425
461
|
if (this.shouldExportElement(modeledElement)) {
|
|
426
462
|
await this.exportModelContainer(model);
|
|
@@ -433,7 +469,8 @@ class IModelExporter {
|
|
|
433
469
|
/** Export the model (the container only) from the source iModel. */
|
|
434
470
|
async exportModelContainer(model) {
|
|
435
471
|
let isUpdate;
|
|
436
|
-
if (undefined !== this._sourceDbChanges) {
|
|
472
|
+
if (undefined !== this._sourceDbChanges) {
|
|
473
|
+
// is changeset information available?
|
|
437
474
|
if (this._sourceDbChanges.model.insertIds.has(model.id)) {
|
|
438
475
|
isUpdate = false;
|
|
439
476
|
}
|
|
@@ -463,8 +500,10 @@ class IModelExporter {
|
|
|
463
500
|
core_bentley_1.Logger.logTrace(loggerCategory, `visitElements=false, skipping exportModelContents(${modelId})`);
|
|
464
501
|
return;
|
|
465
502
|
}
|
|
466
|
-
if (undefined !== this._sourceDbChanges) {
|
|
467
|
-
|
|
503
|
+
if (undefined !== this._sourceDbChanges) {
|
|
504
|
+
// is changeset information available?
|
|
505
|
+
if (!this._sourceDbChanges.model.insertIds.has(modelId) &&
|
|
506
|
+
!this._sourceDbChanges.model.updateIds.has(modelId)) {
|
|
468
507
|
return; // this optimization assumes that the Model changes (LastMod) any time an Element in the Model changes
|
|
469
508
|
}
|
|
470
509
|
}
|
|
@@ -531,7 +570,8 @@ class IModelExporter {
|
|
|
531
570
|
return false;
|
|
532
571
|
}
|
|
533
572
|
}
|
|
534
|
-
if (!this.wantTemplateModels &&
|
|
573
|
+
if (!this.wantTemplateModels &&
|
|
574
|
+
element instanceof core_backend_1.RecipeDefinitionElement) {
|
|
535
575
|
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded RecipeDefinitionElement ${element.id} because wantTemplate=false`);
|
|
536
576
|
return false;
|
|
537
577
|
}
|
|
@@ -552,21 +592,17 @@ class IModelExporter {
|
|
|
552
592
|
core_bentley_1.Logger.logTrace(loggerCategory, `visitElements=false, skipping exportElement(${elementId})`);
|
|
553
593
|
return;
|
|
554
594
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
return this.exportChildElements(elementId);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
const element = this.sourceDb.elements.getElement({ id: elementId, wantGeometry: this.wantGeometry, wantBRepData: this.wantGeometry });
|
|
595
|
+
// are we processing changes?
|
|
596
|
+
const isUpdate = this._sourceDbChanges?.element.insertIds.has(elementId)
|
|
597
|
+
? false
|
|
598
|
+
: this._sourceDbChanges?.element.updateIds.has(elementId)
|
|
599
|
+
? true
|
|
600
|
+
: undefined;
|
|
601
|
+
const element = this.sourceDb.elements.getElement({
|
|
602
|
+
id: elementId,
|
|
603
|
+
wantGeometry: this.wantGeometry,
|
|
604
|
+
wantBRepData: this.wantGeometry,
|
|
605
|
+
});
|
|
570
606
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportElement(${element.id}, "${element.getDisplayLabel()}")${this.getChangeOpSuffix(isUpdate)}`);
|
|
571
607
|
// the order and `await`ing of calls beyond here is depended upon by the IModelTransformer for a current bug workaround
|
|
572
608
|
if (this.shouldExportElement(element)) {
|
|
@@ -606,7 +642,7 @@ class IModelExporter {
|
|
|
606
642
|
*/
|
|
607
643
|
async exportRelationships(baseRelClassFullName) {
|
|
608
644
|
if (!this.visitRelationships) {
|
|
609
|
-
core_bentley_1.Logger.logTrace(loggerCategory,
|
|
645
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "visitRelationships=false, skipping exportRelationships()");
|
|
610
646
|
return;
|
|
611
647
|
}
|
|
612
648
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportRelationships(${baseRelClassFullName})`);
|
|
@@ -617,7 +653,9 @@ class IModelExporter {
|
|
|
617
653
|
await this.sourceDb.withPreparedStatement(sql, async (statement) => {
|
|
618
654
|
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
619
655
|
const relationshipId = statement.getValue(0).getId();
|
|
620
|
-
const relationshipClass = statement
|
|
656
|
+
const relationshipClass = statement
|
|
657
|
+
.getValue(1)
|
|
658
|
+
.getClassNameForClassId();
|
|
621
659
|
await this.exportRelationship(relationshipClass, relationshipId); // must call exportRelationship using the actual classFullName, not baseRelClassFullName
|
|
622
660
|
await this._yieldManager.allowYield();
|
|
623
661
|
}
|
|
@@ -630,7 +668,8 @@ class IModelExporter {
|
|
|
630
668
|
return;
|
|
631
669
|
}
|
|
632
670
|
let isUpdate;
|
|
633
|
-
if (undefined !== this._sourceDbChanges) {
|
|
671
|
+
if (undefined !== this._sourceDbChanges) {
|
|
672
|
+
// is changeset information available?
|
|
634
673
|
if (this._sourceDbChanges.relationship.insertIds.has(relInstanceId)) {
|
|
635
674
|
isUpdate = false;
|
|
636
675
|
}
|
|
@@ -659,7 +698,7 @@ class IModelExporter {
|
|
|
659
698
|
/** Tracks incremental progress */
|
|
660
699
|
async trackProgress() {
|
|
661
700
|
this._progressCounter++;
|
|
662
|
-
if (0 ===
|
|
701
|
+
if (0 === this._progressCounter % this.progressInterval) {
|
|
663
702
|
return this.handler.onProgress();
|
|
664
703
|
}
|
|
665
704
|
}
|
|
@@ -690,9 +729,9 @@ class IModelExporter {
|
|
|
690
729
|
this.visitElements = state.visitElements;
|
|
691
730
|
this.visitRelationships = state.visitRelationships;
|
|
692
731
|
this._excludedCodeSpecNames = new Set(state.excludedCodeSpecNames);
|
|
693
|
-
this._excludedElementIds = core_bentley_1.CompressedId64Set.decompressSet(state.excludedElementIds),
|
|
694
|
-
this._excludedElementCategoryIds = core_bentley_1.CompressedId64Set.decompressSet(state.excludedElementCategoryIds),
|
|
695
|
-
this._excludedElementClasses = new Set(state.excludedElementClassNames.map((c) => this.sourceDb.getJsClass(c)));
|
|
732
|
+
(this._excludedElementIds = core_bentley_1.CompressedId64Set.decompressSet(state.excludedElementIds)),
|
|
733
|
+
(this._excludedElementCategoryIds = core_bentley_1.CompressedId64Set.decompressSet(state.excludedElementCategoryIds)),
|
|
734
|
+
(this._excludedElementClasses = new Set(state.excludedElementClassNames.map((c) => this.sourceDb.getJsClass(c))));
|
|
696
735
|
this._exportElementAspectsStrategy.loadExcludedElementAspectClasses(state.excludedElementAspectClassFullNames);
|
|
697
736
|
this._excludedRelationshipClasses = new Set(state.excludedRelationshipClassNames.map((c) => this.sourceDb.getJsClass(c)));
|
|
698
737
|
this.loadAdditionalStateJson(state.additionalState);
|
|
@@ -715,7 +754,10 @@ class IModelExporter {
|
|
|
715
754
|
excludedElementIds: core_bentley_1.CompressedId64Set.compressSet(this._excludedElementIds),
|
|
716
755
|
excludedElementCategoryIds: core_bentley_1.CompressedId64Set.compressSet(this._excludedElementCategoryIds),
|
|
717
756
|
excludedElementClassNames: Array.from(this._excludedElementClasses, (cls) => cls.classFullName),
|
|
718
|
-
excludedElementAspectClassFullNames: [
|
|
757
|
+
excludedElementAspectClassFullNames: [
|
|
758
|
+
...this._exportElementAspectsStrategy
|
|
759
|
+
.excludedElementAspectClassFullNames,
|
|
760
|
+
],
|
|
719
761
|
excludedRelationshipClassNames: Array.from(this._excludedRelationshipClasses, (cls) => cls.classFullName),
|
|
720
762
|
additionalState: this.getAdditionalStateJson(),
|
|
721
763
|
};
|
|
@@ -724,7 +766,7 @@ class IModelExporter {
|
|
|
724
766
|
exports.IModelExporter = IModelExporter;
|
|
725
767
|
/** Class for holding change information.
|
|
726
768
|
* @beta
|
|
727
|
-
*/
|
|
769
|
+
*/
|
|
728
770
|
class ChangedInstanceOps {
|
|
729
771
|
constructor() {
|
|
730
772
|
this.insertIds = new Set();
|
|
@@ -734,11 +776,11 @@ class ChangedInstanceOps {
|
|
|
734
776
|
/** Initializes the object from IModelJsNative.ChangedInstanceOpsProps. */
|
|
735
777
|
addFromJson(val) {
|
|
736
778
|
if (undefined !== val) {
|
|
737
|
-
if (
|
|
779
|
+
if (undefined !== val.insert && Array.isArray(val.insert))
|
|
738
780
|
val.insert.forEach((id) => this.insertIds.add(id));
|
|
739
|
-
if (
|
|
781
|
+
if (undefined !== val.update && Array.isArray(val.update))
|
|
740
782
|
val.update.forEach((id) => this.updateIds.add(id));
|
|
741
|
-
if (
|
|
783
|
+
if (undefined !== val.delete && Array.isArray(val.delete))
|
|
742
784
|
val.delete.forEach((id) => this.deleteIds.add(id));
|
|
743
785
|
}
|
|
744
786
|
}
|
|
@@ -749,51 +791,174 @@ exports.ChangedInstanceOps = ChangedInstanceOps;
|
|
|
749
791
|
* @beta
|
|
750
792
|
*/
|
|
751
793
|
class ChangedInstanceIds {
|
|
752
|
-
constructor() {
|
|
794
|
+
constructor(db) {
|
|
753
795
|
this.codeSpec = new ChangedInstanceOps();
|
|
754
796
|
this.model = new ChangedInstanceOps();
|
|
755
797
|
this.element = new ChangedInstanceOps();
|
|
756
798
|
this.aspect = new ChangedInstanceOps();
|
|
757
799
|
this.relationship = new ChangedInstanceOps();
|
|
758
800
|
this.font = new ChangedInstanceOps();
|
|
801
|
+
this._db = db;
|
|
802
|
+
}
|
|
803
|
+
async setupECClassIds() {
|
|
804
|
+
this._codeSpecSubclassIds = new Set();
|
|
805
|
+
this._modelSubclassIds = new Set();
|
|
806
|
+
this._elementSubclassIds = new Set();
|
|
807
|
+
this._aspectSubclassIds = new Set();
|
|
808
|
+
this._relationshipSubclassIds = new Set();
|
|
809
|
+
const addECClassIdsToSet = async (setToModify, baseClass) => {
|
|
810
|
+
for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})`)) {
|
|
811
|
+
setToModify.add(row.ECInstanceId);
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
const promises = [
|
|
815
|
+
addECClassIdsToSet(this._codeSpecSubclassIds, "BisCore.CodeSpec"),
|
|
816
|
+
addECClassIdsToSet(this._modelSubclassIds, "BisCore.Model"),
|
|
817
|
+
addECClassIdsToSet(this._elementSubclassIds, "BisCore.Element"),
|
|
818
|
+
addECClassIdsToSet(this._aspectSubclassIds, "BisCore.ElementUniqueAspect"),
|
|
819
|
+
addECClassIdsToSet(this._aspectSubclassIds, "BisCore.ElementMultiAspect"),
|
|
820
|
+
addECClassIdsToSet(this._relationshipSubclassIds, "BisCore.ElementRefersToElements"),
|
|
821
|
+
];
|
|
822
|
+
await Promise.all(promises);
|
|
823
|
+
}
|
|
824
|
+
get _ecClassIdsInitialized() {
|
|
825
|
+
return (this._codeSpecSubclassIds &&
|
|
826
|
+
this._modelSubclassIds &&
|
|
827
|
+
this._elementSubclassIds &&
|
|
828
|
+
this._aspectSubclassIds &&
|
|
829
|
+
this._relationshipSubclassIds);
|
|
830
|
+
}
|
|
831
|
+
isRelationship(ecClassId) {
|
|
832
|
+
return this._relationshipSubclassIds?.has(ecClassId);
|
|
833
|
+
}
|
|
834
|
+
isCodeSpec(ecClassId) {
|
|
835
|
+
return this._codeSpecSubclassIds?.has(ecClassId);
|
|
836
|
+
}
|
|
837
|
+
isAspect(ecClassId) {
|
|
838
|
+
return this._aspectSubclassIds?.has(ecClassId);
|
|
839
|
+
}
|
|
840
|
+
isModel(ecClassId) {
|
|
841
|
+
return this._modelSubclassIds?.has(ecClassId);
|
|
842
|
+
}
|
|
843
|
+
isElement(ecClassId) {
|
|
844
|
+
return this._elementSubclassIds?.has(ecClassId);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Adds the provided [[ChangedECInstance]] to the appropriate set of changes by class type (codeSpec, model, element, aspect, or relationship) maintained by this instance of ChangedInstanceIds.
|
|
848
|
+
* If the same ECInstanceId is seen multiple times, the changedInstanceIds will be modified accordingly, i.e. if an id 'x' was updated but now we see 'x' was deleted, we will remove 'x'
|
|
849
|
+
* from the set of updatedIds and add it to the set of deletedIds for the appropriate class type.
|
|
850
|
+
* @param change ChangedECInstance which has the ECInstanceId, changeType (insert, update, delete) and ECClassId of the changed entity
|
|
851
|
+
*/
|
|
852
|
+
async addChange(change) {
|
|
853
|
+
if (!this._ecClassIdsInitialized)
|
|
854
|
+
await this.setupECClassIds();
|
|
855
|
+
const ecClassId = change.ECClassId ?? change.$meta?.fallbackClassId;
|
|
856
|
+
if (ecClassId === undefined)
|
|
857
|
+
throw new Error(`ECClassId was not found for id: ${change.ECInstanceId}! Table is : ${change?.$meta?.tables}`);
|
|
858
|
+
const changeType = change.$meta?.op;
|
|
859
|
+
if (changeType === undefined)
|
|
860
|
+
throw new Error(`ChangeType was undefined for id: ${change.ECInstanceId}.`);
|
|
861
|
+
if (this.isRelationship(ecClassId))
|
|
862
|
+
this.handleChange(this.relationship, changeType, change.ECInstanceId);
|
|
863
|
+
else if (this.isCodeSpec(ecClassId))
|
|
864
|
+
this.handleChange(this.codeSpec, changeType, change.ECInstanceId);
|
|
865
|
+
else if (this.isAspect(ecClassId))
|
|
866
|
+
this.handleChange(this.aspect, changeType, change.ECInstanceId);
|
|
867
|
+
else if (this.isModel(ecClassId))
|
|
868
|
+
this.handleChange(this.model, changeType, change.ECInstanceId);
|
|
869
|
+
else if (this.isElement(ecClassId))
|
|
870
|
+
this.handleChange(this.element, changeType, change.ECInstanceId);
|
|
871
|
+
}
|
|
872
|
+
handleChange(changedInstanceOps, changeType, id) {
|
|
873
|
+
// if changeType is a delete and we already have the id in the inserts then we can remove the id from the inserts.
|
|
874
|
+
// if changeType is a delete and we already have the id in the updates then we can remove the id from the updates AND add it to the deletes.
|
|
875
|
+
// if changeType is an insert and we already have the id in the deletes then we can remove the id from the deletes AND add it to the inserts.
|
|
876
|
+
if (changeType === "Inserted") {
|
|
877
|
+
changedInstanceOps.insertIds.add(id);
|
|
878
|
+
changedInstanceOps.deleteIds.delete(id);
|
|
879
|
+
}
|
|
880
|
+
else if (changeType === "Updated") {
|
|
881
|
+
if (!changedInstanceOps.insertIds.has(id))
|
|
882
|
+
changedInstanceOps.updateIds.add(id);
|
|
883
|
+
}
|
|
884
|
+
else if (changeType === "Deleted") {
|
|
885
|
+
// If we've inserted the entity at some point already and now we're seeing a delete. We can simply remove the entity from our inserted ids without adding it to deletedIds.
|
|
886
|
+
if (changedInstanceOps.insertIds.has(id))
|
|
887
|
+
changedInstanceOps.insertIds.delete(id);
|
|
888
|
+
else {
|
|
889
|
+
changedInstanceOps.updateIds.delete(id);
|
|
890
|
+
changedInstanceOps.deleteIds.add(id);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
759
893
|
}
|
|
760
894
|
/**
|
|
761
895
|
* Initializes a new ChangedInstanceIds object with information taken from a range of changesets.
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
* @note Modified element information will be taken from a range of changesets. First changeset in a range will be the 'firstChangesetId', the last will be whichever changeset the 'iModel' briefcase is currently opened on.
|
|
767
|
-
*/
|
|
768
|
-
static async initialize(accessTokenOrOpts, iModel, startChangesetId) {
|
|
769
|
-
const opts = typeof accessTokenOrOpts === "object"
|
|
770
|
-
? accessTokenOrOpts
|
|
771
|
-
: {
|
|
772
|
-
accessToken: accessTokenOrOpts,
|
|
773
|
-
iModel: iModel,
|
|
774
|
-
startChangeset: { id: startChangesetId },
|
|
775
|
-
};
|
|
776
|
-
const accessToken = opts.accessToken;
|
|
896
|
+
*/
|
|
897
|
+
static async initialize(opts) {
|
|
898
|
+
if ("changedInstanceIds" in opts)
|
|
899
|
+
return opts.changedInstanceIds;
|
|
777
900
|
const iModelId = opts.iModel.iModelId;
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
901
|
+
const accessToken = opts.accessToken;
|
|
902
|
+
const startChangeset = "startChangeset" in opts ? opts.startChangeset : undefined;
|
|
903
|
+
const changesetRanges = startChangeset !== undefined
|
|
904
|
+
? [
|
|
905
|
+
[
|
|
906
|
+
startChangeset.index ??
|
|
907
|
+
(await core_backend_1.IModelHost.hubAccess.queryChangeset({
|
|
908
|
+
iModelId,
|
|
909
|
+
changeset: {
|
|
910
|
+
id: startChangeset.id ?? opts.iModel.changeset.id,
|
|
911
|
+
},
|
|
912
|
+
accessToken,
|
|
913
|
+
})).index,
|
|
914
|
+
opts.iModel.changeset.index ??
|
|
915
|
+
(await core_backend_1.IModelHost.hubAccess.queryChangeset({
|
|
916
|
+
iModelId,
|
|
917
|
+
changeset: { id: opts.iModel.changeset.id },
|
|
918
|
+
accessToken,
|
|
919
|
+
})).index,
|
|
920
|
+
],
|
|
921
|
+
]
|
|
922
|
+
: "changesetRanges" in opts
|
|
923
|
+
? opts.changesetRanges
|
|
924
|
+
: undefined;
|
|
925
|
+
const csFileProps = changesetRanges !== undefined
|
|
926
|
+
? (await Promise.all(changesetRanges.map(async ([first, end]) => core_backend_1.IModelHost.hubAccess.downloadChangesets({
|
|
927
|
+
accessToken,
|
|
928
|
+
iModelId,
|
|
929
|
+
range: { first, end },
|
|
930
|
+
targetDir: core_backend_1.BriefcaseManager.getChangeSetsPath(iModelId),
|
|
931
|
+
})))).flat()
|
|
932
|
+
: "csFileProps" in opts
|
|
933
|
+
? opts.csFileProps
|
|
934
|
+
: undefined;
|
|
935
|
+
if (csFileProps === undefined)
|
|
936
|
+
return undefined;
|
|
937
|
+
const changedInstanceIds = new ChangedInstanceIds(opts.iModel);
|
|
938
|
+
const relationshipECClassIdsToSkip = new Set();
|
|
939
|
+
for await (const row of opts.iModel.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)")) {
|
|
940
|
+
relationshipECClassIdsToSkip.add(row.ECInstanceId);
|
|
941
|
+
}
|
|
942
|
+
for (const csFile of csFileProps) {
|
|
943
|
+
const csReader = core_backend_1.SqliteChangesetReader.openFile({
|
|
944
|
+
fileName: csFile.pathname,
|
|
945
|
+
db: opts.iModel,
|
|
946
|
+
disableSchemaCheck: true,
|
|
947
|
+
});
|
|
948
|
+
const csAdaptor = new core_backend_1.ChangesetECAdaptor(csReader);
|
|
949
|
+
const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier();
|
|
950
|
+
while (csAdaptor.step()) {
|
|
951
|
+
ecChangeUnifier.appendFrom(csAdaptor);
|
|
952
|
+
}
|
|
953
|
+
const changes = [...ecChangeUnifier.instances];
|
|
954
|
+
for (const change of changes) {
|
|
955
|
+
if (change.ECClassId !== undefined &&
|
|
956
|
+
relationshipECClassIdsToSkip.has(change.ECClassId))
|
|
957
|
+
continue;
|
|
958
|
+
await changedInstanceIds.addChange(change);
|
|
959
|
+
}
|
|
960
|
+
csReader.close();
|
|
961
|
+
}
|
|
797
962
|
return changedInstanceIds;
|
|
798
963
|
}
|
|
799
964
|
}
|