iTwin / imodel-transformer

API for exporting an iModel's parts and also importing them into another iModel
MIT License
3 stars 2 forks source link

Add a posibility to re-add deleted elements with preserved ids #200

Closed JulijaRamoskiene closed 1 month ago

JulijaRamoskiene commented 2 months ago

Requested functionality

Make it possible to run transformation that would re-add deleted elements with preserved element ids. I think this behavior should be supported trough current preserveElementIdsForFiltering option.

Desired behavior:

Sample code:

it("process() with preserveElementIdsForFiltering set to true should re-add deleted element with same id", async () => {
    const sourceDbPath = IModelTransformerTestUtils.prepareOutputFile(
      "IModelTransformer",
      "PreserveIdOnTestModel-Source.bim"
    );
    const sourceDb = SnapshotDb.createEmpty(sourceDbPath, {
      rootSubject: { name: "iModelA"},
    });
    Subject.insert(sourceDb, IModel.rootSubjectId, "Subject1");
    sourceDb.saveChanges();

    const targetDbPath = IModelTransformerTestUtils.prepareOutputFile(
      "IModelTransformer",
      "PreserveIdOnTestModel-Target.bim"
    );
    const targetDb = SnapshotDb.createEmpty(targetDbPath, {
      rootSubject: sourceDb.rootSubject,
    });

    // Execute process() so that elements from source are copied to target
    const transformer = new IModelTransformer(sourceDb, targetDb, {
      preserveElementIdsForFiltering: true,
    });
    await transformer.process();
    targetDb.saveChanges();

    let sourceContent = await getAllElementsInvariants(sourceDb);
    let targetContent = await getAllElementsInvariants(targetDb);
    expect(targetContent).to.deep.equal(sourceContent);

    // Delete subject from target
    const code = Subject.createCode(targetDb, IModel.rootSubjectId, "Subject1");;
    const targetSubjectId = targetDb.elements.queryElementIdByCode(code);
    expect(targetSubjectId).to.not.be.undefined;
    targetDb.elements.deleteElement(targetSubjectId!);
    targetDb.saveChanges();

    // Calling process() for second time with option to preserve elements in hopes of restoring deleted element
     const secondTransformer = new IModelTransformer(sourceDb, targetDb, {
      preserveElementIdsForFiltering: true,
    });
    await secondTransformer.process(); // should not throw error: duplicate code (65547) 
    targetDb.saveChanges();

    sourceContent = await getAllElementsInvariants(sourceDb);
    targetContent = await getAllElementsInvariants(targetDb);
    expect(targetContent).to.deep.equal(sourceContent);

    sourceDb.close();
    targetDb.close();
  });
  it("process() with preserveElementIdsForFiltering set to true should not throw when called on 2 identical iModels", async () => {
    const seedDb = SnapshotDb.openFile(
      TestUtils.IModelTestUtils.resolveAssetFile("CompatibilityTestSeed.bim")
    );
    const sourceDbPath = IModelTransformerTestUtils.prepareOutputFile(
      "IModelTransformer",
      "PreserveIdOnTestModel-Source.bim"
    );
    // transforming the seed to an empty will update it to the latest bis from the new target
    // which minimizes differences we'd otherwise need to filter later
    const sourceDb = SnapshotDb.createEmpty(sourceDbPath, {
      rootSubject: seedDb.rootSubject,
    });
    const seedTransformer = new IModelTransformer(seedDb, sourceDb);
    await seedTransformer.process();
    sourceDb.saveChanges();

    const targetDbPath = IModelTransformerTestUtils.prepareOutputFile(
      "IModelTransformer",
      "PreserveIdOnTestModel-Target.bim"
    );
    const targetDb = SnapshotDb.createEmpty(targetDbPath, {
      rootSubject: sourceDb.rootSubject,
    });

    // Calling process() for first time will add all elements from source to target
    const transformer = new IModelTransformer(sourceDb, targetDb, {
      preserveElementIdsForFiltering: true,
    });
    await transformer.process();
    targetDb.saveChanges();

    // should not throw error: duplicate code (65547)
    const thirdTransformer = new IModelTransformer(sourceDb, targetDb, {
      preserveElementIdsForFiltering: true,
    });
    await thirdTransformer.process();
    targetDb.saveChanges();

    const sourceContent = await getAllElementsInvariants(sourceDb);
    const targetContent = await getAllElementsInvariants(targetDb);
    expect(targetContent).to.deep.equal(sourceContent);

    sourceDb.close();
    targetDb.close();
  });
derbynn commented 1 month ago

@JulijaRamoskiene we are working on this issue. We noticed that the first unit test passes with the existing code, meaning the deleted elements are re-added.

image We also noticed that for the first unit test, the line - // should not throw error: duplicate code (65547). My understanding is that you are saying that the line currently throws a duplicate error, but it should not throw that error. But that will not be possible since we are deleting the subject from the target. Could you please clarify if you meant something else?

JulijaRamoskiene commented 1 month ago

This error is thrown because of other elements that exist in target, not because of subject. I want elements to be re-added to target with same id, meaning that in this scenario, subject with same id should be added to target: image

As I described above:

Do you plan to introduce new option that would control what should happen with conflicting elements when preserveElementIds is set?(to throw "duplicated code" vs to update it. ) Or we will change how preserveElementIds is acting now? Update: It was decided that new option is not needed. preserveElementIds will now update existing elements with matching id instead of throwing error.