Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark soft conflict if not contiguous with trunk #1187

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

ktuite
Copy link
Member

@ktuite ktuite commented Sep 16, 2024

Closes getodk/central#698

I copied this branch tracking logic directly from Frontend, and there's probably more there than necessary for backend, but I just wanted to see it in action. We could also consider having backend return more of the details that frontend is currently computing about branch continuity.

What has been done to verify that this works as intended?

Why is this the best possible solution? Were any other approaches considered?

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

Does this change require updates to the API documentation? If so, please update docs/api.yaml as part of this PR.

Before submitting this PR, please make sure you have:

  • run make test and confirmed all checks still pass OR confirm CircleCI build passes
  • verified that any code from external sources are properly credited in comments or that everything is internally sourced

@matthew-white
Copy link
Member

We could also consider having backend return more of the details that frontend is currently computing about branch continuity.

I think this is a pretty interesting idea. I don't think it's necessary for this particular issue, but I think it's something we should keep in mind for the future.

Copy link
Member

@matthew-white matthew-white left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this branch tracking logic directly from Frontend, and there's probably more there than necessary for backend, but I just wanted to see it in action.

I'm going to pare back aspects of the Branch class and related logic to only what Backend needs, which I think will make the Backend code clearer. However, even pared back, I like the Branch class here. It moves the branch logic into a separate part of the code from the rest of the getWithConflictDetails() function, which is already fairly complex. I also think it's very possible that we'll want to grow the Branch class in the future.

Before I push my commit, I thought I'd leave a few comments about specific things I'm changing.

@@ -417,6 +475,26 @@ const getWithConflictDetails = (defs, audits, relevantToConflict) => {

const relevantBaseVersions = new Set();

// build up branches
const branches = new Map();
for (const version of defs) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ends up looping over defs twice, which I don't think we need to do. I think we can build up result and branches in the same loop, as no version/def needs to look ahead to a later version, including for branch information.

if (existingBranch == null) {
const newBranch = new Branch(version, defs[0]);
branches.set(branchId, newBranch);
version.branch = newBranch;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we do it in Frontend, but I think we should avoid mutating version here unless it's necessary.

const branches = new Map();
for (const version of defs) {
const { branchId } = version;
if (branchId != null && version.branchBaseVersion != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added version.branchBaseVersion != null to Frontend mostly as a temporary measure, since at the time, there were submissions where the entity creation got a branch ID. I think we can remove the condition here.

@matthew-white
Copy link
Member

This PR makes changes to the conflict property of entity versions, but in some cases, I think we also need to make changes to the conflict property of the entity itself. For example, say an entity had a conflict, but was then resolved. A new offline update comes in that applies to the current version. That all seems good, and currently, the conflict property of the entity will be null. However, with the change to the conflict logic, the new version may actually be a conflict: the conflict property of the new version will be 'soft' if its base version is not contiguous with the trunk version. In that case, the conflict property of the entity itself should also be 'soft'. However, right now I'm pretty sure it's null. I modified the first test to try to create an example:

it('should mark an update that is not contiguous with its trunk version as a soft conflict', testOfflineEntities(async (service, container) => {
  const asAlice = await service.login('alice');
  const branchId = uuid();

  // Update existing entity on server (change age from 22 to 24)
  await asAlice.patch('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc?baseVersion=1')
    .send({ data: { age: '24' } })
    .expect(200);

  // Send update (change status from null to arrived)
  await asAlice.post('/v1/projects/1/forms/offlineEntity/submissions')
    .send(testData.instances.offlineEntity.one
      .replace('branchId=""', `branchId="${branchId}"`)
    )
    .set('Content-Type', 'application/xml')
    .expect(200);
  await exhaust(container);

  await asAlice.patch('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc?resolve=true&baseVersion=3')
    .expect(200);

  // Send second update (change age from 22 to 26)
  await asAlice.post('/v1/projects/1/forms/offlineEntity/submissions')
    .send(testData.instances.offlineEntity.one
      .replace('branchId=""', `branchId="${branchId}"`)
      .replace('one', 'one-update2')
      .replace('baseVersion="1"', 'baseVersion="2"')
      .replace('<status>arrived</status>', '<age>26</age>')
    )
    .set('Content-Type', 'application/xml')
    .expect(200);
  await exhaust(container);

  await asAlice.get('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc/versions')
    .then(({ body: versions }) => {
      versions.map(v => v.conflict).should.eql([null, null, 'soft', 'soft']);
    });

  await asAlice.get('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc')
    .then(({ body: entity }) => {
      // This assertion currently fails.
      should(entity.conflict).equal('soft');
    });
}));

@matthew-white
Copy link
Member

Also, @ktuite, I've pushed my commit. Would you mind taking a look at it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update conflict logic for offline entities
2 participants