@itwin/imodel-transformer 2.0.0-dev.3 → 2.0.0-dev.30
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/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +24 -18
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/ChangesetScanner.d.ts +60 -0
- package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
- package/lib/cjs/ChangesetScanner.js +101 -0
- package/lib/cjs/ChangesetScanner.js.map +1 -0
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +8 -6
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +60 -38
- package/lib/cjs/ECReferenceTypesCache.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 +12 -12
- package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +4 -8
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +6 -24
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts +4 -4
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +17 -16
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +3 -3
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +70 -54
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +33 -23
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +200 -152
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +32 -24
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +199 -127
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +94 -186
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +502 -1075
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/IModelTransformerError.d.ts +74 -0
- package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
- package/lib/cjs/IModelTransformerError.js +83 -0
- package/lib/cjs/IModelTransformerError.js.map +1 -0
- package/lib/cjs/ProvenanceManager.d.ts +165 -0
- package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
- package/lib/cjs/ProvenanceManager.js +735 -0
- package/lib/cjs/ProvenanceManager.js.map +1 -0
- package/lib/cjs/SyncTypeResolver.d.ts +34 -0
- package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
- package/lib/cjs/SyncTypeResolver.js +96 -0
- package/lib/cjs/SyncTypeResolver.js.map +1 -0
- package/lib/cjs/TransformerLoggerCategory.d.ts +2 -1
- package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +1 -0
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/imodel-transformer.d.ts +5 -0
- package/lib/cjs/imodel-transformer.d.ts.map +1 -1
- package/lib/cjs/imodel-transformer.js +26 -7
- package/lib/cjs/imodel-transformer.js.map +1 -1
- package/package.json +22 -17
|
@@ -15,6 +15,8 @@ const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
|
|
|
15
15
|
const TransformerLoggerCategory_1 = require("./TransformerLoggerCategory");
|
|
16
16
|
const nodeAssert = require("assert");
|
|
17
17
|
const ExportElementAspectsWithElementsStrategy_1 = require("./ExportElementAspectsWithElementsStrategy");
|
|
18
|
+
const ChangesetScanner_1 = require("./ChangesetScanner");
|
|
19
|
+
const IModelTransformerError_1 = require("./IModelTransformerError");
|
|
18
20
|
const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelExporter;
|
|
19
21
|
/** Handles the events generated by IModelExporter.
|
|
20
22
|
* @note Change information is available when `IModelExportHandler` methods are invoked via [IModelExporter.exportChanges]($transformer), but not available when invoked via [IModelExporter.exportAll]($transformer).
|
|
@@ -26,7 +28,7 @@ class IModelExportHandler {
|
|
|
26
28
|
/** If `true` is returned, then the CodeSpec will be exported.
|
|
27
29
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
28
30
|
*/
|
|
29
|
-
shouldExportCodeSpec(_codeSpec) {
|
|
31
|
+
async shouldExportCodeSpec(_codeSpec) {
|
|
30
32
|
return true;
|
|
31
33
|
}
|
|
32
34
|
/** Called when a CodeSpec should be exported.
|
|
@@ -34,37 +36,37 @@ class IModelExportHandler {
|
|
|
34
36
|
* @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.
|
|
35
37
|
* @note This should be overridden to actually do the export.
|
|
36
38
|
*/
|
|
37
|
-
onExportCodeSpec(_codeSpec, _isUpdate) { }
|
|
39
|
+
async onExportCodeSpec(_codeSpec, _isUpdate) { }
|
|
38
40
|
/** Called when a font should be exported.
|
|
39
41
|
* @param font The font to export
|
|
40
42
|
* @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.
|
|
41
43
|
* @note This should be overridden to actually do the export.
|
|
42
44
|
*/
|
|
43
|
-
onExportFont(_font, _isUpdate) { }
|
|
45
|
+
async onExportFont(_font, _isUpdate) { }
|
|
44
46
|
/** Called when a model should be exported.
|
|
45
47
|
* @param model The model to export
|
|
46
48
|
* @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.
|
|
47
49
|
* @note This should be overridden to actually do the export.
|
|
48
50
|
*/
|
|
49
|
-
onExportModel(_model, _isUpdate) { }
|
|
51
|
+
async onExportModel(_model, _isUpdate) { }
|
|
50
52
|
/** Called when a model should be deleted. */
|
|
51
|
-
onDeleteModel(_modelId) { }
|
|
53
|
+
async onDeleteModel(_modelId) { }
|
|
52
54
|
/** If `true` is returned, then the element will be exported.
|
|
53
55
|
* @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`.
|
|
54
56
|
*/
|
|
55
|
-
shouldExportElement(_element) {
|
|
57
|
+
async shouldExportElement(_element) {
|
|
56
58
|
return true;
|
|
57
59
|
}
|
|
58
60
|
/** Called when element is skipped instead of exported.
|
|
59
61
|
* @note When an element is skipped, exporter will not export any of its child elements. Because of this, [[onSkipElement]] will not be invoked for any children of a "skipped" element.
|
|
60
62
|
*/
|
|
61
|
-
onSkipElement(_elementId) { }
|
|
63
|
+
async onSkipElement(_elementId) { }
|
|
62
64
|
/** Called when an element should be exported.
|
|
63
65
|
* @param element The element to export
|
|
64
66
|
* @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.
|
|
65
67
|
* @note This should be overridden to actually do the export.
|
|
66
68
|
*/
|
|
67
|
-
onExportElement(_element, _isUpdate) { }
|
|
69
|
+
async onExportElement(_element, _isUpdate) { }
|
|
68
70
|
/**
|
|
69
71
|
* Do any asynchronous actions before exporting an element
|
|
70
72
|
* @note Do not implement this handler manually, it is internal, it will be removed.
|
|
@@ -73,11 +75,11 @@ class IModelExportHandler {
|
|
|
73
75
|
*/
|
|
74
76
|
async preExportElement(_element) { }
|
|
75
77
|
/** Called when an element should be deleted. */
|
|
76
|
-
onDeleteElement(_elementId) { }
|
|
78
|
+
async onDeleteElement(_elementId) { }
|
|
77
79
|
/** If `true` is returned, then the ElementAspect will be exported.
|
|
78
80
|
* @note This method can optionally be overridden to exclude an individual ElementAspect from the export. The base implementation always returns `true`.
|
|
79
81
|
*/
|
|
80
|
-
shouldExportElementAspect(_aspect) {
|
|
82
|
+
async shouldExportElementAspect(_aspect) {
|
|
81
83
|
return true;
|
|
82
84
|
}
|
|
83
85
|
/** Called when an ElementUniqueAspect should be exported.
|
|
@@ -85,15 +87,15 @@ class IModelExportHandler {
|
|
|
85
87
|
* @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.
|
|
86
88
|
* @note This should be overridden to actually do the export.
|
|
87
89
|
*/
|
|
88
|
-
onExportElementUniqueAspect(_aspect, _isUpdate) { }
|
|
90
|
+
async onExportElementUniqueAspect(_aspect, _isUpdate) { }
|
|
89
91
|
/** Called when ElementMultiAspects should be exported.
|
|
90
92
|
* @note This should be overridden to actually do the export.
|
|
91
93
|
*/
|
|
92
|
-
onExportElementMultiAspects(_aspects) { }
|
|
94
|
+
async onExportElementMultiAspects(_aspects) { }
|
|
93
95
|
/** If `true` is returned, then the relationship will be exported.
|
|
94
96
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
95
97
|
*/
|
|
96
|
-
shouldExportRelationship(_relationship) {
|
|
98
|
+
async shouldExportRelationship(_relationship) {
|
|
97
99
|
return true;
|
|
98
100
|
}
|
|
99
101
|
/** Called when a Relationship should be exported.
|
|
@@ -101,13 +103,13 @@ class IModelExportHandler {
|
|
|
101
103
|
* @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.
|
|
102
104
|
* @note This should be overridden to actually do the export.
|
|
103
105
|
*/
|
|
104
|
-
onExportRelationship(_relationship, _isUpdate) { }
|
|
106
|
+
async onExportRelationship(_relationship, _isUpdate) { }
|
|
105
107
|
/** Called when a relationship should be deleted. */
|
|
106
|
-
onDeleteRelationship(_relInstanceId) { }
|
|
108
|
+
async onDeleteRelationship(_relInstanceId) { }
|
|
107
109
|
/** If `true` is returned, then the schema will be exported.
|
|
108
110
|
* @note This method can optionally be overridden to exclude an individual schema from the export. The base implementation always returns `true`.
|
|
109
111
|
*/
|
|
110
|
-
shouldExportSchema(_schemaKey) {
|
|
112
|
+
async shouldExportSchema(_schemaKey) {
|
|
111
113
|
return true;
|
|
112
114
|
}
|
|
113
115
|
/** Called when a schema should be exported.
|
|
@@ -175,7 +177,13 @@ class IModelExporter {
|
|
|
175
177
|
/** The handler called by this IModelExporter. */
|
|
176
178
|
get handler() {
|
|
177
179
|
if (undefined === this._handler) {
|
|
178
|
-
|
|
180
|
+
core_bentley_1.ITwinError.throwError({
|
|
181
|
+
iTwinErrorId: {
|
|
182
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
183
|
+
key: IModelTransformerError_1.IModelTransformerError.ExportHandlerNotRegistered,
|
|
184
|
+
},
|
|
185
|
+
message: "IModelExportHandler not registered",
|
|
186
|
+
});
|
|
179
187
|
}
|
|
180
188
|
return this._handler;
|
|
181
189
|
}
|
|
@@ -198,9 +206,9 @@ class IModelExporter {
|
|
|
198
206
|
constructor(sourceDb, elementAspectsStrategy = ExportElementAspectsWithElementsStrategy_1.ExportElementAspectsWithElementsStrategy) {
|
|
199
207
|
this.sourceDb = sourceDb;
|
|
200
208
|
this._exportElementAspectsStrategy = new elementAspectsStrategy(this.sourceDb, {
|
|
201
|
-
onExportElementMultiAspects: (aspects) => this.handler.onExportElementMultiAspects(aspects),
|
|
202
|
-
onExportElementUniqueAspect: (aspect, isUpdate) => this.handler.onExportElementUniqueAspect(aspect, isUpdate),
|
|
203
|
-
shouldExportElementAspect: (aspect) => this.handler.shouldExportElementAspect(aspect),
|
|
209
|
+
onExportElementMultiAspects: async (aspects) => this.handler.onExportElementMultiAspects(aspects),
|
|
210
|
+
onExportElementUniqueAspect: async (aspect, isUpdate) => this.handler.onExportElementUniqueAspect(aspect, isUpdate),
|
|
211
|
+
shouldExportElementAspect: async (aspect) => this.handler.shouldExportElementAspect(aspect),
|
|
204
212
|
trackProgress: async () => this.trackProgress(),
|
|
205
213
|
});
|
|
206
214
|
}
|
|
@@ -262,6 +270,7 @@ class IModelExporter {
|
|
|
262
270
|
}
|
|
263
271
|
/** Export changes from the source iModel.
|
|
264
272
|
* Inserts, updates, and deletes are determined by inspecting the changeset(s).
|
|
273
|
+
* @throws [[ITwinError]] identified by [[IModelTransformerError.NoChangesets]] if the source iModel has no changesets and no custom changes. Call [[exportAll]] to export all content.
|
|
265
274
|
* @note To form a range of versions to process, set `startChangesetId` for the start (inclusive) of the desired
|
|
266
275
|
* range and open the source iModel as of the end (inclusive) of the desired range.
|
|
267
276
|
* @note the changedInstanceIds are just for this call to exportChanges, so you must continue to pass it in
|
|
@@ -269,23 +278,45 @@ class IModelExporter {
|
|
|
269
278
|
*/
|
|
270
279
|
async exportChanges(args) {
|
|
271
280
|
if (!this.sourceDb.isBriefcaseDb())
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
281
|
+
core_bentley_1.ITwinError.throwError({
|
|
282
|
+
iTwinErrorId: {
|
|
283
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
284
|
+
key: IModelTransformerError_1.IModelTransformerError.ExportChangesRequiresBriefcase,
|
|
285
|
+
},
|
|
286
|
+
message: "Must be a briefcase to export changes",
|
|
287
|
+
});
|
|
288
|
+
let initOpts = args ?? {};
|
|
289
|
+
const hasExplicitChangeSource = "csFileProps" in initOpts ||
|
|
290
|
+
"changedInstanceIds" in initOpts ||
|
|
291
|
+
"changesetRanges" in initOpts ||
|
|
292
|
+
"startChangeset" in initOpts;
|
|
293
|
+
const currentChangesetId = this.sourceDb.changeset.id;
|
|
294
|
+
if (!hasExplicitChangeSource && currentChangesetId !== "") {
|
|
295
|
+
initOpts = {
|
|
296
|
+
...initOpts,
|
|
297
|
+
startChangeset: { id: currentChangesetId },
|
|
298
|
+
};
|
|
277
299
|
}
|
|
278
|
-
const startChangeset = args && "startChangeset" in args ? args.startChangeset : undefined;
|
|
279
|
-
const initOpts = {
|
|
280
|
-
startChangeset: { id: startChangeset?.id },
|
|
281
|
-
};
|
|
282
300
|
await this.initialize(initOpts);
|
|
301
|
+
if (currentChangesetId === "" && !this.sourceDbChanges?.hasChanges) {
|
|
302
|
+
core_bentley_1.ITwinError.throwError({
|
|
303
|
+
iTwinErrorId: {
|
|
304
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
305
|
+
key: IModelTransformerError_1.IModelTransformerError.NoChangesets,
|
|
306
|
+
},
|
|
307
|
+
message: "Cannot export changes because the source iModel has no changesets or custom changes. Call exportAll() to export all content.",
|
|
308
|
+
});
|
|
309
|
+
}
|
|
283
310
|
// _sourceDbChanges are initialized in this.initialize
|
|
284
311
|
nodeAssert(this._sourceDbChanges !== undefined, "sourceDbChanges must be initialized.");
|
|
285
312
|
await this.exportCodeSpecs();
|
|
286
313
|
await this.exportFonts();
|
|
287
314
|
if (initOpts.skipPropagateChangesToRootElements) {
|
|
288
|
-
|
|
315
|
+
// The root Subject is in the RepositoryModel. Traverse its children
|
|
316
|
+
// separately, then export other top-level repository elements while
|
|
317
|
+
// excluding the root so no element is visited twice.
|
|
318
|
+
await this.exportChildElements(core_common_1.IModel.rootSubjectId);
|
|
319
|
+
await this.exportModelContents(core_common_1.IModel.repositoryModelId, core_backend_1.Element.classFullName, true);
|
|
289
320
|
await this.exportSubModels(core_common_1.IModel.repositoryModelId);
|
|
290
321
|
}
|
|
291
322
|
else {
|
|
@@ -297,7 +328,7 @@ class IModelExporter {
|
|
|
297
328
|
if (this.visitElements) {
|
|
298
329
|
// must delete models first since they have a constraint on the submodeling element which may also be deleted
|
|
299
330
|
for (const modelId of this._sourceDbChanges.model.deleteIds) {
|
|
300
|
-
this.handler.onDeleteModel(modelId);
|
|
331
|
+
await this.handler.onDeleteModel(modelId);
|
|
301
332
|
}
|
|
302
333
|
for (const elementId of this._sourceDbChanges.element.deleteIds) {
|
|
303
334
|
// We don't know how the handler wants to handle deletions, and we don't have enough information
|
|
@@ -307,7 +338,7 @@ class IModelExporter {
|
|
|
307
338
|
// In the future, the handler may be responsible for doing the work of finding out which elements were cascade deleted,
|
|
308
339
|
// and returning them for the exporter to use to avoid double-deleting with error ignoring
|
|
309
340
|
try {
|
|
310
|
-
this.handler.onDeleteElement(elementId);
|
|
341
|
+
await this.handler.onDeleteElement(elementId);
|
|
311
342
|
}
|
|
312
343
|
catch (err) {
|
|
313
344
|
const isMissingErr = err instanceof core_common_1.IModelError &&
|
|
@@ -320,7 +351,7 @@ class IModelExporter {
|
|
|
320
351
|
if (this.visitRelationships) {
|
|
321
352
|
for (const relInstanceId of this._sourceDbChanges.relationship
|
|
322
353
|
.deleteIds) {
|
|
323
|
-
this.handler.onDeleteRelationship(relInstanceId);
|
|
354
|
+
await this.handler.onDeleteRelationship(relInstanceId);
|
|
324
355
|
}
|
|
325
356
|
}
|
|
326
357
|
// Enable consecutive exportChanges runs without the need to re-instantiate the exporter.
|
|
@@ -345,26 +376,33 @@ class IModelExporter {
|
|
|
345
376
|
`}
|
|
346
377
|
ORDER BY ECInstanceId
|
|
347
378
|
`;
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
379
|
+
const schemaKeysToExport = [];
|
|
380
|
+
for await (const row of this.sourceDb.createQueryReader(sql, undefined, {
|
|
381
|
+
usePrimaryConn: true,
|
|
382
|
+
})) {
|
|
383
|
+
const schemaName = row[0];
|
|
384
|
+
const versionMajor = row[1];
|
|
385
|
+
const versionWrite = row[2];
|
|
386
|
+
const versionMinor = row[3];
|
|
387
|
+
const schemaKey = new ecschema_metadata_1.SchemaKey(schemaName, new ecschema_metadata_1.ECVersion(versionMajor, versionWrite, versionMinor));
|
|
388
|
+
if (await this.handler.shouldExportSchema(schemaKey)) {
|
|
389
|
+
schemaKeysToExport.push(schemaKey);
|
|
360
390
|
}
|
|
361
|
-
}
|
|
362
|
-
if (
|
|
391
|
+
}
|
|
392
|
+
if (schemaKeysToExport.length === 0)
|
|
363
393
|
return;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
394
|
+
await Promise.all(schemaKeysToExport.map(async (schemaKey) => {
|
|
395
|
+
const schema = await this.sourceDb.schemaContext.getSchema(schemaKey);
|
|
396
|
+
if (!schema) {
|
|
397
|
+
core_bentley_1.ITwinError.throwError({
|
|
398
|
+
iTwinErrorId: {
|
|
399
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
400
|
+
key: IModelTransformerError_1.IModelTransformerError.SchemaLoadFailed,
|
|
401
|
+
},
|
|
402
|
+
message: `Failed to load schema: ${schemaKey.name}`,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `exportSchema(${schemaKey.name})`);
|
|
368
406
|
return this.handler.onExportSchema(schema);
|
|
369
407
|
}));
|
|
370
408
|
}
|
|
@@ -378,15 +416,11 @@ class IModelExporter {
|
|
|
378
416
|
async exportCodeSpecs() {
|
|
379
417
|
core_bentley_1.Logger.logTrace(loggerCategory, "exportCodeSpecs()");
|
|
380
418
|
const sql = "SELECT Name FROM BisCore:CodeSpec ORDER BY ECInstanceId";
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const codeSpecName = statement.getValue(0).getString();
|
|
387
|
-
await this.exportCodeSpecByName(codeSpecName);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
419
|
+
for await (const row of this.sourceDb.createQueryReader(sql, undefined, {
|
|
420
|
+
usePrimaryConn: true,
|
|
421
|
+
})) {
|
|
422
|
+
await this.exportCodeSpecByName(row[0]);
|
|
423
|
+
}
|
|
390
424
|
}
|
|
391
425
|
/** Export a single CodeSpec from the source iModel.
|
|
392
426
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
@@ -412,9 +446,9 @@ class IModelExporter {
|
|
|
412
446
|
return;
|
|
413
447
|
}
|
|
414
448
|
// CodeSpec has passed standard exclusion rules, now give handler a chance to accept/reject export
|
|
415
|
-
if (this.handler.shouldExportCodeSpec(codeSpec)) {
|
|
449
|
+
if (await this.handler.shouldExportCodeSpec(codeSpec)) {
|
|
416
450
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportCodeSpec(${codeSpecName})${this.getChangeOpSuffix(isUpdate)}`);
|
|
417
|
-
this.handler.onExportCodeSpec(codeSpec, isUpdate);
|
|
451
|
+
await this.handler.onExportCodeSpec(codeSpec, isUpdate);
|
|
418
452
|
return this.trackProgress();
|
|
419
453
|
}
|
|
420
454
|
}
|
|
@@ -430,35 +464,54 @@ class IModelExporter {
|
|
|
430
464
|
*/
|
|
431
465
|
async exportFonts() {
|
|
432
466
|
core_bentley_1.Logger.logTrace(loggerCategory, "exportFonts()");
|
|
433
|
-
for (const font of this.sourceDb.
|
|
434
|
-
await this.
|
|
467
|
+
for (const font of this.sourceDb.fonts.queryMappedFamilies()) {
|
|
468
|
+
await this.exportFontByFontProps(font);
|
|
435
469
|
}
|
|
436
470
|
}
|
|
437
471
|
/** Export a single font from the source iModel.
|
|
438
|
-
* @note
|
|
472
|
+
* @note multiple fonts can have the same font name, if a font with a specific type is needed use exportFontByFontFamilyDescriptor.
|
|
473
|
+
* If not this will only export the first font with this type in the db.
|
|
439
474
|
*/
|
|
440
475
|
async exportFontByName(fontName) {
|
|
441
476
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontByName(${fontName})`);
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
477
|
+
const fontId = this.sourceDb.fonts.findId({
|
|
478
|
+
name: fontName,
|
|
479
|
+
});
|
|
480
|
+
if (undefined !== fontId) {
|
|
481
|
+
await this.exportFontByNumber(fontId);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/** Export a single font from the source iModel.
|
|
485
|
+
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
486
|
+
*/
|
|
487
|
+
async exportFontByFontProps(fontProps) {
|
|
488
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontByFamily(${fontProps.name}, ${fontProps.type})`);
|
|
489
|
+
await this.handler.onExportFont(fontProps, true);
|
|
490
|
+
return this.trackProgress();
|
|
491
|
+
}
|
|
492
|
+
/** Export a single font from the source iModel.
|
|
493
|
+
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
494
|
+
*/
|
|
495
|
+
async exportFontByFontFamilyDescriptor(fontFamily) {
|
|
496
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontByFamilyDescriptor(${fontFamily.name}, ${fontFamily.type})`);
|
|
497
|
+
const fontId = await this.sourceDb.fonts.acquireId(fontFamily);
|
|
498
|
+
if (undefined !== fontId) {
|
|
499
|
+
await this.exportFontByFontProps({ ...fontFamily, id: fontId });
|
|
445
500
|
}
|
|
446
501
|
}
|
|
447
502
|
/** Export a single font from the source iModel.
|
|
448
503
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
449
504
|
*/
|
|
450
505
|
async exportFontByNumber(fontNumber) {
|
|
451
|
-
/**
|
|
452
|
-
*
|
|
506
|
+
/** ChangedInstanceIds contains EC-mapped changes, but the font table is not EC-mapped.
|
|
507
|
+
* Always export fonts because their changes are therefore absent from sourceDbChanges.
|
|
453
508
|
* 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.
|
|
454
509
|
* See https://github.com/iTwin/imodel-transformer/pull/135 for removed code.
|
|
455
510
|
*/
|
|
456
|
-
const isUpdate = true;
|
|
457
511
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontById(${fontNumber})`);
|
|
458
|
-
const font = this.sourceDb.
|
|
512
|
+
const font = this.sourceDb.fonts.findDescriptor(fontNumber);
|
|
459
513
|
if (undefined !== font) {
|
|
460
|
-
this.
|
|
461
|
-
return this.trackProgress();
|
|
514
|
+
await this.exportFontByFontFamilyDescriptor(font);
|
|
462
515
|
}
|
|
463
516
|
}
|
|
464
517
|
/** Export the model container, contents, and sub-models from the source iModel.
|
|
@@ -475,7 +528,7 @@ class IModelExporter {
|
|
|
475
528
|
wantBRepData: this.wantGeometry,
|
|
476
529
|
});
|
|
477
530
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportModel(${modeledElementId})`);
|
|
478
|
-
if (this.shouldExportElement(modeledElement)) {
|
|
531
|
+
if (await this.shouldExportElement(modeledElement)) {
|
|
479
532
|
await this.exportModelContainer(model);
|
|
480
533
|
if (this.visitElements) {
|
|
481
534
|
await this.exportModelContents(modeledElementId);
|
|
@@ -498,7 +551,7 @@ class IModelExporter {
|
|
|
498
551
|
return; // not in changeset, don't export
|
|
499
552
|
}
|
|
500
553
|
}
|
|
501
|
-
this.handler.onExportModel(model, isUpdate);
|
|
554
|
+
await this.handler.onExportModel(model, isUpdate);
|
|
502
555
|
return this.trackProgress();
|
|
503
556
|
}
|
|
504
557
|
_yieldManager = new core_bentley_1.YieldManager();
|
|
@@ -533,19 +586,16 @@ class IModelExporter {
|
|
|
533
586
|
else {
|
|
534
587
|
sql = `SELECT ECInstanceId FROM ${elementClassFullName} WHERE Parent.Id IS NULL AND Model.Id=:modelId ORDER BY ECInstanceId`;
|
|
535
588
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
await this._yieldManager.allowYield();
|
|
547
|
-
}
|
|
548
|
-
});
|
|
589
|
+
const params = new core_common_1.QueryBinder().bindId("modelId", modelId);
|
|
590
|
+
if (skipRootSubject) {
|
|
591
|
+
params.bindId("rootSubjectId", core_common_1.IModel.rootSubjectId);
|
|
592
|
+
}
|
|
593
|
+
for await (const row of this.sourceDb.createQueryReader(sql, params, {
|
|
594
|
+
usePrimaryConn: true,
|
|
595
|
+
})) {
|
|
596
|
+
await this.exportElement(row.id);
|
|
597
|
+
await this._yieldManager.allowYield();
|
|
598
|
+
}
|
|
549
599
|
}
|
|
550
600
|
/** Export the sub-models directly below the specified model.
|
|
551
601
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
@@ -555,22 +605,19 @@ class IModelExporter {
|
|
|
555
605
|
const definitionModelIds = [];
|
|
556
606
|
const otherModelIds = [];
|
|
557
607
|
const sql = `SELECT ECInstanceId FROM ${core_backend_1.Model.classFullName} WHERE ParentModel.Id=:parentModelId ORDER BY ECInstanceId`;
|
|
558
|
-
|
|
559
|
-
this.sourceDb.
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (model instanceof core_backend_1.DefinitionModel) {
|
|
567
|
-
definitionModelIds.push(modelId);
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
otherModelIds.push(modelId);
|
|
571
|
-
}
|
|
608
|
+
const params = new core_common_1.QueryBinder().bindId("parentModelId", parentModelId);
|
|
609
|
+
for await (const row of this.sourceDb.createQueryReader(sql, params, {
|
|
610
|
+
usePrimaryConn: true,
|
|
611
|
+
})) {
|
|
612
|
+
const modelId = row.id;
|
|
613
|
+
const model = this.sourceDb.models.getModel(modelId);
|
|
614
|
+
if (model instanceof core_backend_1.DefinitionModel) {
|
|
615
|
+
definitionModelIds.push(modelId);
|
|
572
616
|
}
|
|
573
|
-
|
|
617
|
+
else {
|
|
618
|
+
otherModelIds.push(modelId);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
574
621
|
// export DefinitionModels before other types of Models
|
|
575
622
|
for (const definitionModelId of definitionModelIds) {
|
|
576
623
|
await this.exportModel(definitionModelId);
|
|
@@ -583,7 +630,7 @@ class IModelExporter {
|
|
|
583
630
|
* This considers the standard IModelExporter exclusion rules plus calls [IModelExportHandler.shouldExportElement]($transformer) for any custom exclusion rules.
|
|
584
631
|
* @note This method is called from within [[exportChanges]] and [[exportAll]], so usually does not need to be called directly.
|
|
585
632
|
*/
|
|
586
|
-
shouldExportElement(element) {
|
|
633
|
+
async shouldExportElement(element) {
|
|
587
634
|
if (this._excludedElementIds.has(element.id)) {
|
|
588
635
|
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded element ${element.id} by Id`);
|
|
589
636
|
return false;
|
|
@@ -619,7 +666,7 @@ class IModelExporter {
|
|
|
619
666
|
// Return early if the elementId is already in the excludedElementIds, that way we don't need to load the element from the db.
|
|
620
667
|
if (this._excludedElementIds.has(elementId)) {
|
|
621
668
|
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded element ${elementId} by Id`);
|
|
622
|
-
this.handler.onSkipElement(elementId);
|
|
669
|
+
await this.handler.onSkipElement(elementId);
|
|
623
670
|
return;
|
|
624
671
|
}
|
|
625
672
|
// are we processing changes?
|
|
@@ -628,6 +675,10 @@ class IModelExporter {
|
|
|
628
675
|
: this._sourceDbChanges?.element.updateIds.has(elementId)
|
|
629
676
|
? true
|
|
630
677
|
: undefined;
|
|
678
|
+
// Short-circuit: element is not in the changeset, skip its own export but still visit children
|
|
679
|
+
if (undefined !== this._sourceDbChanges && undefined === isUpdate) {
|
|
680
|
+
return this.exportChildElements(elementId);
|
|
681
|
+
}
|
|
631
682
|
const element = this.sourceDb.elements.getElement({
|
|
632
683
|
id: elementId,
|
|
633
684
|
wantGeometry: this.wantGeometry,
|
|
@@ -635,15 +686,15 @@ class IModelExporter {
|
|
|
635
686
|
});
|
|
636
687
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportElement(${element.id}, "${element.getDisplayLabel()}")${this.getChangeOpSuffix(isUpdate)}`);
|
|
637
688
|
// the order and `await`ing of calls beyond here is depended upon by the IModelTransformer for a current bug workaround
|
|
638
|
-
if (this.shouldExportElement(element)) {
|
|
689
|
+
if (await this.shouldExportElement(element)) {
|
|
639
690
|
await this.handler.preExportElement(element);
|
|
640
|
-
this.handler.onExportElement(element, isUpdate);
|
|
691
|
+
await this.handler.onExportElement(element, isUpdate);
|
|
641
692
|
await this.trackProgress();
|
|
642
693
|
await this._exportElementAspectsStrategy.exportElementAspectsForElement(elementId);
|
|
643
694
|
return this.exportChildElements(elementId);
|
|
644
695
|
}
|
|
645
696
|
else {
|
|
646
|
-
this.handler.onSkipElement(element.id);
|
|
697
|
+
await this.handler.onSkipElement(element.id);
|
|
647
698
|
}
|
|
648
699
|
}
|
|
649
700
|
/** Export the child elements of the specified element from the source iModel.
|
|
@@ -676,23 +727,18 @@ class IModelExporter {
|
|
|
676
727
|
return;
|
|
677
728
|
}
|
|
678
729
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportRelationships(${baseRelClassFullName})`);
|
|
679
|
-
const sql = `SELECT r.ECInstanceId, r.ECClassId FROM ${baseRelClassFullName} r
|
|
730
|
+
const sql = `SELECT r.ECInstanceId, ec_className(r.ECClassId, 's.c') as className FROM ${baseRelClassFullName} r
|
|
680
731
|
JOIN bis.Element s ON s.ECInstanceId = r.SourceECInstanceId
|
|
681
732
|
JOIN bis.Element t ON t.ECInstanceId = r.TargetECInstanceId
|
|
682
733
|
WHERE s.ECInstanceId IS NOT NULL AND t.ECInstanceId IS NOT NULL`;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
.getClassNameForClassId();
|
|
692
|
-
await this.exportRelationship(relationshipClass, relationshipId); // must call exportRelationship using the actual classFullName, not baseRelClassFullName
|
|
693
|
-
await this._yieldManager.allowYield();
|
|
694
|
-
}
|
|
695
|
-
});
|
|
734
|
+
for await (const row of this.sourceDb.createQueryReader(sql, undefined, {
|
|
735
|
+
usePrimaryConn: true,
|
|
736
|
+
})) {
|
|
737
|
+
const relationshipId = row.id;
|
|
738
|
+
const relationshipClass = row.className;
|
|
739
|
+
await this.exportRelationship(relationshipClass, relationshipId); // must call exportRelationship using the actual classFullName, not baseRelClassFullName
|
|
740
|
+
await this._yieldManager.allowYield();
|
|
741
|
+
}
|
|
696
742
|
}
|
|
697
743
|
/** Export a relationship from the source iModel. */
|
|
698
744
|
async exportRelationship(relClassFullName, relInstanceId) {
|
|
@@ -723,8 +769,8 @@ class IModelExporter {
|
|
|
723
769
|
}
|
|
724
770
|
}
|
|
725
771
|
// relationship has passed standard exclusion rules, now give handler a chance to accept/reject export
|
|
726
|
-
if (this.handler.shouldExportRelationship(relationship)) {
|
|
727
|
-
this.handler.onExportRelationship(relationship, isUpdate);
|
|
772
|
+
if (await this.handler.shouldExportRelationship(relationship)) {
|
|
773
|
+
await this.handler.onExportRelationship(relationship, isUpdate);
|
|
728
774
|
await this.trackProgress();
|
|
729
775
|
}
|
|
730
776
|
}
|
|
@@ -795,7 +841,7 @@ class ChangedInstanceIds {
|
|
|
795
841
|
this._relationshipSubclassIds = new Set();
|
|
796
842
|
this._relationshipSubclassIdsToSkip = new Set();
|
|
797
843
|
const addECClassIdsToSet = async (setToModify, baseClass) => {
|
|
798
|
-
for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})
|
|
844
|
+
for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})`, undefined, { usePrimaryConn: true })) {
|
|
799
845
|
setToModify.add(row.ECInstanceId);
|
|
800
846
|
}
|
|
801
847
|
};
|
|
@@ -845,20 +891,32 @@ class ChangedInstanceIds {
|
|
|
845
891
|
!this.font.isEmpty);
|
|
846
892
|
}
|
|
847
893
|
/**
|
|
848
|
-
* Adds the provided [[
|
|
894
|
+
* Adds the provided [[ChangeInstance]] to the appropriate set of changes by class type (codeSpec, model, element, aspect, or relationship) maintained by this instance of ChangedInstanceIds.
|
|
849
895
|
* 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'
|
|
850
896
|
* from the set of updatedIds and add it to the set of deletedIds for the appropriate class type.
|
|
851
|
-
* @param change
|
|
897
|
+
* @param change Changed EC instance with the ID, operation, and EC class ID of the changed entity.
|
|
852
898
|
*/
|
|
853
899
|
async addChange(change) {
|
|
854
900
|
if (!this._ecClassIdsInitialized)
|
|
855
901
|
await this.setupECClassIds();
|
|
856
|
-
const ecClassId = change.ECClassId
|
|
902
|
+
const ecClassId = change.ECClassId;
|
|
857
903
|
if (ecClassId === undefined)
|
|
858
|
-
|
|
859
|
-
|
|
904
|
+
core_bentley_1.ITwinError.throwError({
|
|
905
|
+
iTwinErrorId: {
|
|
906
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
907
|
+
key: IModelTransformerError_1.IModelTransformerError.ChangedInstanceMetadataMissing,
|
|
908
|
+
},
|
|
909
|
+
message: `ECClassId was not found for id: ${change.ECInstanceId}! Table is : ${change.$meta.tables}`,
|
|
910
|
+
});
|
|
911
|
+
const changeType = change.$meta.op;
|
|
860
912
|
if (changeType === undefined)
|
|
861
|
-
|
|
913
|
+
core_bentley_1.ITwinError.throwError({
|
|
914
|
+
iTwinErrorId: {
|
|
915
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
916
|
+
key: IModelTransformerError_1.IModelTransformerError.ChangedInstanceMetadataMissing,
|
|
917
|
+
},
|
|
918
|
+
message: `ChangeType was undefined for id: ${change.ECInstanceId}.`,
|
|
919
|
+
});
|
|
862
920
|
if (this._relationshipSubclassIdsToSkip?.has(ecClassId))
|
|
863
921
|
return;
|
|
864
922
|
if (this.isRelationship(ecClassId))
|
|
@@ -958,7 +1016,9 @@ class ChangedInstanceIds {
|
|
|
958
1016
|
SELECT parentId FROM hierarchy where parentId is not null
|
|
959
1017
|
`;
|
|
960
1018
|
const parentModelIds = new Set();
|
|
961
|
-
for await (const row of this._db.createQueryReader(ecQuery, params
|
|
1019
|
+
for await (const row of this._db.createQueryReader(ecQuery, params, {
|
|
1020
|
+
usePrimaryConn: true,
|
|
1021
|
+
})) {
|
|
962
1022
|
// Transformer handles update as insert when element does not exist in target.
|
|
963
1023
|
// Which means that in scenario where child and parent model are filtered out from target,
|
|
964
1024
|
// and child element is inserted trough custom change, its parent model will be marked as updated.
|
|
@@ -976,7 +1036,9 @@ class ChangedInstanceIds {
|
|
|
976
1036
|
WHERE InVirtualSet(:elementIds, TargetECInstanceId)
|
|
977
1037
|
OR InVirtualSet(:elementIds, SourceECInstanceId)`;
|
|
978
1038
|
const queryBinder = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
|
|
979
|
-
const queryReader = this._db.createQueryReader(ecQuery, queryBinder
|
|
1039
|
+
const queryReader = this._db.createQueryReader(ecQuery, queryBinder, {
|
|
1040
|
+
usePrimaryConn: true,
|
|
1041
|
+
});
|
|
980
1042
|
for await (const row of queryReader) {
|
|
981
1043
|
this.handleChange(this.relationship, "Inserted", row.ECInstanceId);
|
|
982
1044
|
}
|
|
@@ -988,7 +1050,9 @@ class ChangedInstanceIds {
|
|
|
988
1050
|
]) {
|
|
989
1051
|
const ecQuery = `Select ECInstanceId from ${aspectClassName} where InVirtualSet(:elementIds, Element.Id)`;
|
|
990
1052
|
const queryBinder = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
|
|
991
|
-
const queryReader = this._db.createQueryReader(ecQuery, queryBinder
|
|
1053
|
+
const queryReader = this._db.createQueryReader(ecQuery, queryBinder, {
|
|
1054
|
+
usePrimaryConn: true,
|
|
1055
|
+
});
|
|
992
1056
|
for await (const row of queryReader) {
|
|
993
1057
|
this.addCustomAspectChange("Inserted", row.toArray()[0]);
|
|
994
1058
|
}
|
|
@@ -1057,23 +1121,7 @@ class ChangedInstanceIds {
|
|
|
1057
1121
|
if (csFileProps === undefined)
|
|
1058
1122
|
return undefined;
|
|
1059
1123
|
const changedInstanceIds = new ChangedInstanceIds(opts.iModel);
|
|
1060
|
-
|
|
1061
|
-
const csReader = core_backend_1.SqliteChangesetReader.openFile({
|
|
1062
|
-
fileName: csFile.pathname,
|
|
1063
|
-
db: opts.iModel,
|
|
1064
|
-
disableSchemaCheck: true,
|
|
1065
|
-
});
|
|
1066
|
-
const csAdaptor = new core_backend_1.ChangesetECAdaptor(csReader);
|
|
1067
|
-
const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier(opts.iModel);
|
|
1068
|
-
while (csAdaptor.step()) {
|
|
1069
|
-
ecChangeUnifier.appendFrom(csAdaptor);
|
|
1070
|
-
}
|
|
1071
|
-
const changes = [...ecChangeUnifier.instances];
|
|
1072
|
-
for (const change of changes) {
|
|
1073
|
-
await changedInstanceIds.addChange(change);
|
|
1074
|
-
}
|
|
1075
|
-
csReader.close();
|
|
1076
|
-
}
|
|
1124
|
+
await ChangesetScanner_1.ChangesetScanner.scan(opts.iModel, csFileProps, changedInstanceIds);
|
|
1077
1125
|
return changedInstanceIds;
|
|
1078
1126
|
}
|
|
1079
1127
|
}
|