@itwin/imodel-transformer 2.0.0-dev.67 → 2.0.0-dev.69

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.
@@ -18,6 +18,7 @@ const ElementAspectExportProcessor_1 = require("./ElementAspectExportProcessor")
18
18
  const ElementAspectExportCoordinator_1 = require("./ElementAspectExportCoordinator");
19
19
  const IModelTransformerError_1 = require("./IModelTransformerError");
20
20
  const ChangesetScanner_1 = require("./ChangesetScanner");
21
+ const SourceClassKinds_1 = require("./SourceClassKinds");
21
22
  const ChangedElementForest_1 = require("./ChangedElementForest");
22
23
  const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelExporter;
23
24
  /** Handles the events generated by IModelExporter.
@@ -1387,12 +1388,7 @@ class ChangedInstanceIds {
1387
1388
  aspect = new ChangedInstanceOps();
1388
1389
  relationship = new ChangedInstanceOps();
1389
1390
  font = new ChangedInstanceOps();
1390
- _codeSpecSubclassIds;
1391
- _modelSubclassIds;
1392
- _elementSubclassIds;
1393
- _aspectSubclassIds;
1394
- _relationshipSubclassIds;
1395
- _relationshipSubclassIdsToSkip;
1391
+ _classKinds;
1396
1392
  _aspectOwnerElementIds = new Set();
1397
1393
  /** Element IDs that own the aspects represented by `aspect` changes.
1398
1394
  * @internal
@@ -1404,52 +1400,6 @@ class ChangedInstanceIds {
1404
1400
  constructor(db) {
1405
1401
  this._db = db;
1406
1402
  }
1407
- async setupECClassIds() {
1408
- this._codeSpecSubclassIds = new Set();
1409
- this._modelSubclassIds = new Set();
1410
- this._elementSubclassIds = new Set();
1411
- this._aspectSubclassIds = new Set();
1412
- this._relationshipSubclassIds = new Set();
1413
- this._relationshipSubclassIdsToSkip = new Set();
1414
- const addECClassIdsToSet = async (setToModify, baseClass) => {
1415
- for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})`, undefined, { usePrimaryConn: true })) {
1416
- setToModify.add(row.ECInstanceId);
1417
- }
1418
- };
1419
- const promises = [
1420
- addECClassIdsToSet(this._codeSpecSubclassIds, "BisCore.CodeSpec"),
1421
- addECClassIdsToSet(this._modelSubclassIds, "BisCore.Model"),
1422
- addECClassIdsToSet(this._elementSubclassIds, "BisCore.Element"),
1423
- addECClassIdsToSet(this._aspectSubclassIds, "BisCore.ElementUniqueAspect"),
1424
- addECClassIdsToSet(this._aspectSubclassIds, "BisCore.ElementMultiAspect"),
1425
- addECClassIdsToSet(this._relationshipSubclassIds, "BisCore.ElementRefersToElements"),
1426
- addECClassIdsToSet(this._relationshipSubclassIdsToSkip, "BisCore.ElementDrivesElement"),
1427
- ];
1428
- await Promise.all(promises);
1429
- }
1430
- get _ecClassIdsInitialized() {
1431
- return (this._codeSpecSubclassIds &&
1432
- this._modelSubclassIds &&
1433
- this._elementSubclassIds &&
1434
- this._aspectSubclassIds &&
1435
- this._relationshipSubclassIds &&
1436
- this._relationshipSubclassIdsToSkip);
1437
- }
1438
- isRelationship(ecClassId) {
1439
- return this._relationshipSubclassIds?.has(ecClassId);
1440
- }
1441
- isCodeSpec(ecClassId) {
1442
- return this._codeSpecSubclassIds?.has(ecClassId);
1443
- }
1444
- isAspect(ecClassId) {
1445
- return this._aspectSubclassIds?.has(ecClassId);
1446
- }
1447
- isModel(ecClassId) {
1448
- return this._modelSubclassIds?.has(ecClassId);
1449
- }
1450
- isElement(ecClassId) {
1451
- return this._elementSubclassIds?.has(ecClassId);
1452
- }
1453
1403
  /** Checks if there are any changes.
1454
1404
  * @returns true if there are any changes in the ChangedInstanceIds object.
1455
1405
  */
@@ -1470,9 +1420,30 @@ class ChangedInstanceIds {
1470
1420
  async addChange(change) {
1471
1421
  return this.recordChange(change, undefined);
1472
1422
  }
1423
+ /**
1424
+ * Adds a change read from somewhere other than a changeset, such as a precomputed cache, like [[addChange]]
1425
+ * does for a changeset reader's [ChangeInstance]($backend). Add changes in the order they were made, so that,
1426
+ * for example, an insert followed by a delete of the same instance cancels out.
1427
+ * @throws [ITwinError]($bentley) with key `AspectOwnerRequired` if a deleted aspect has no `aspectOwnerElementId`.
1428
+ * @beta
1429
+ */
1430
+ async addChangeRecord(change) {
1431
+ if (change.op === "Deleted" && change.aspectOwnerElementId === undefined) {
1432
+ this._classKinds ??= await SourceClassKinds_1.SourceClassKinds.query(this._db);
1433
+ const kind = this._classKinds.kindOf(change.ecClassId);
1434
+ // The source no longer has the aspect, so without its owner the target aspect would silently stay.
1435
+ if (kind === "aspect" || kind === "externalSourceAspect")
1436
+ core_bentley_1.ITwinError.throwError({
1437
+ iTwinErrorId: {
1438
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
1439
+ key: IModelTransformerError_1.IModelTransformerError.AspectOwnerRequired,
1440
+ },
1441
+ message: `Deleted ElementAspect change ${change.id} requires the owning element ID.`,
1442
+ });
1443
+ }
1444
+ return this.applyChangeRecord(change, undefined);
1445
+ }
1473
1446
  async recordChange(change, unresolvedAspectIds) {
1474
- if (!this._ecClassIdsInitialized)
1475
- await this.setupECClassIds();
1476
1447
  const ecClassId = change.ECClassId;
1477
1448
  if (ecClassId === undefined)
1478
1449
  core_bentley_1.ITwinError.throwError({
@@ -1491,29 +1462,43 @@ class ChangedInstanceIds {
1491
1462
  },
1492
1463
  message: `ChangeType was undefined for id: ${change.ECInstanceId}.`,
1493
1464
  });
1494
- if (this._relationshipSubclassIdsToSkip?.has(ecClassId))
1495
- return;
1496
- if (this.isRelationship(ecClassId))
1497
- this.handleChange(this.relationship, changeType, change.ECInstanceId);
1498
- else if (this.isCodeSpec(ecClassId))
1499
- this.handleChange(this.codeSpec, changeType, change.ECInstanceId);
1500
- else if (this.isAspect(ecClassId)) {
1501
- let ownerElementId = change.Element?.Id;
1502
- if (ownerElementId === undefined) {
1503
- if (unresolvedAspectIds !== undefined)
1504
- unresolvedAspectIds.add(change.ECInstanceId);
1505
- else
1506
- ownerElementId = this.tryGetAspectOwnerElementId(change.ECInstanceId);
1507
- }
1508
- if (ownerElementId !== undefined) {
1509
- this._aspectOwnerElementIds.add(ownerElementId);
1465
+ return this.applyChangeRecord({
1466
+ id: change.ECInstanceId,
1467
+ ecClassId,
1468
+ op: changeType,
1469
+ aspectOwnerElementId: change.Element?.Id,
1470
+ }, unresolvedAspectIds);
1471
+ }
1472
+ async applyChangeRecord(change, unresolvedAspectIds) {
1473
+ // Await only the first time, so later changes are classified without yielding.
1474
+ this._classKinds ??= await SourceClassKinds_1.SourceClassKinds.query(this._db);
1475
+ const { id, ecClassId, op } = change;
1476
+ switch (this._classKinds.kindOf(ecClassId)) {
1477
+ case "relationship":
1478
+ return this.handleChange(this.relationship, op, id);
1479
+ case "codeSpec":
1480
+ return this.handleChange(this.codeSpec, op, id);
1481
+ case "aspect":
1482
+ case "externalSourceAspect": {
1483
+ let ownerElementId = change.aspectOwnerElementId;
1484
+ if (ownerElementId === undefined) {
1485
+ if (unresolvedAspectIds !== undefined)
1486
+ unresolvedAspectIds.add(id);
1487
+ else
1488
+ ownerElementId = this.tryGetAspectOwnerElementId(id);
1489
+ }
1490
+ if (ownerElementId !== undefined)
1491
+ this._aspectOwnerElementIds.add(ownerElementId);
1492
+ return this.handleChange(this.aspect, op, id);
1510
1493
  }
1511
- this.handleChange(this.aspect, changeType, change.ECInstanceId);
1494
+ case "model":
1495
+ return this.handleChange(this.model, op, id);
1496
+ case "element":
1497
+ return this.handleChange(this.element, op, id);
1498
+ case "skippedRelationship":
1499
+ case undefined:
1500
+ return;
1512
1501
  }
1513
- else if (this.isModel(ecClassId))
1514
- this.handleChange(this.model, changeType, change.ECInstanceId);
1515
- else if (this.isElement(ecClassId))
1516
- this.handleChange(this.element, changeType, change.ECInstanceId);
1517
1502
  }
1518
1503
  tryGetAspectOwnerElementId(aspectId) {
1519
1504
  try {
@@ -1751,18 +1736,17 @@ class ChangedInstanceIds {
1751
1736
  ? opts.changesetRanges
1752
1737
  : undefined;
1753
1738
  const csFileProps = changesetRanges !== undefined
1754
- ? (await Promise.all(changesetRanges.map(async ([first, end]) => core_backend_1.BriefcaseManager.downloadChangesets({
1755
- iModelId,
1756
- range: { first, end },
1757
- targetDir: core_backend_1.BriefcaseManager.getChangeSetsPath(iModelId),
1758
- })))).flat()
1739
+ ? (await ChangesetScanner_1.ChangesetScanner.download(opts.iModel, changesetRanges)).flat()
1759
1740
  : "csFileProps" in opts
1760
1741
  ? opts.csFileProps
1761
1742
  : undefined;
1762
1743
  if (csFileProps === undefined)
1763
1744
  return undefined;
1764
1745
  const changedInstanceIds = new ChangedInstanceIds(opts.iModel);
1765
- await ChangesetScanner_1.ChangesetScanner.scan(opts.iModel, csFileProps, changedInstanceIds);
1746
+ await ChangesetScanner_1.ChangesetScanner.scan(opts.iModel, [csFileProps], {
1747
+ changedInstanceIds,
1748
+ collectDeletionRecords: false,
1749
+ });
1766
1750
  return changedInstanceIds;
1767
1751
  }
1768
1752
  /** Record an ordered batch, then resolve missing aspect owners against current source state.