Skip to content

Feature getodk/central#1010: Added bulk delete and restore entpoints for the Entities#1580

Merged
sadiqkhoja merged 3 commits intogetodk:masterfrom
sadiqkhoja:features/bulk-delete-entities
Sep 10, 2025
Merged

Feature getodk/central#1010: Added bulk delete and restore entpoints for the Entities#1580
sadiqkhoja merged 3 commits intogetodk:masterfrom
sadiqkhoja:features/bulk-delete-entities

Conversation

@sadiqkhoja
Copy link
Contributor

@sadiqkhoja sadiqkhoja commented Aug 6, 2025

Closes getodk/central#1010

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

Added tests and manual verification with the frontend.

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

Follows the existing pattern. One decision that I took is to return 200 even if provided UUIDs don't exist. My reasoning is that it makes the endpoint idempotent, returning 404 would be semantically wrong http status as endpoint does exists. In future, if there is a requirement then we can return the list of UUIDs that were actually deleted.

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?

I don't see any risk of regression.

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

Yes

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

@sadiqkhoja sadiqkhoja force-pushed the features/bulk-delete-entities branch from c86f9a4 to f561efe Compare August 6, 2025 16:50
@sadiqkhoja sadiqkhoja marked this pull request as ready for review August 8, 2025 01:31
@sadiqkhoja sadiqkhoja requested a review from ktuite August 8, 2025 01:31
Copy link
Member

@ktuite ktuite left a comment

Choose a reason for hiding this comment

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

Ideas about behavior if not all the ids provided can be deleted or restored:

  1. The audit log should probably just log the ones that have successfully been acted on, and not every ID included in a request.
  2. If a bulk delete or restore action has no impact because everything was already deleted or just doesn't exist, maybe no audit event should be logged.
  3. The API could return the number of entities affected so you knew if you sent 500 IDs that all 500 worked or if there was some other issue with the bulk operation.

@@ -40,6 +40,12 @@ info:

Copy link
Member

Choose a reason for hiding this comment

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

Add entity.bulk.delete and entity.bulk.restore and remove from nonverbose audit logs as in #1442

Comment on lines +11 to +15
// This index supports the query pattern: (audits.details->'entityUuids') @> '[entity_uuid]'
// and is filtered to only bulk delete and restore actions.
await db.raw(`CREATE INDEX audits_details_entityuuids_gin_index
ON audits USING gin ((details->'entityUuids'))
WHERE action IN ('entity.bulk.delete', 'entity.bulk.restore')`);
Copy link
Member

Choose a reason for hiding this comment

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

How does this index relate to this other index?

CREATE INDEX audits_details_entityUuids ON audits USING gin ((details -> 'entityUuids') jsonb_path_ops) WHERE ACTION = 'entity.purge';

https://github.com/getodk/central-backend/blob/master/lib/model/migrations/20241226-01-indices-for-purging-entities.js#L14

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch! I have updated the existing index.

})
.expect(200)
.then(({ body }) => {
body.success.should.be.true();
Copy link
Member

Choose a reason for hiding this comment

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

Returning success here seems ok but it looks like this action will also get logged in the audit log with the 12345678-1234-4123-8234-nonexistent entity id.

I think it should only log the uuids of the entities it successfully deleted, if possible.

Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to return the count of the actual # of entities deleted or restored?

@ktuite
Copy link
Member

ktuite commented Aug 8, 2025

One more note, in the frontend, a little bit of work needs to be done to use these new events.

In this screenshot, the delete and restore events are weird. (I thought I changed something about this code so it would show the raw event action if it didn't know how to format it right.)

Screenshot 2025-08-08 at 4 47 08 PM

Copy link
Member

@ktuite ktuite left a comment

Choose a reason for hiding this comment

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

Looks good! Please update filename of migration before merging!

@sadiqkhoja sadiqkhoja force-pushed the features/bulk-delete-entities branch from 19d5248 to deea88c Compare August 27, 2025 21:49
* return count of affected entities
* log only UUIDs of affected entities and nothing if no entity is affected
* Use existing index just update its where clause
* Update API docs with the new audit actions
* Update nonverbose action list in Audit API
@sadiqkhoja sadiqkhoja force-pushed the features/bulk-delete-entities branch from deea88c to 6137a1c Compare September 3, 2025 18:31
@sadiqkhoja sadiqkhoja merged commit eba36a5 into getodk:master Sep 10, 2025
7 checks passed
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.

Bulk delete entities

2 participants