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

#710: Restore Entities, Purge Entities, ability to see deleted Entities #1349

Merged
merged 11 commits into from
Jan 30, 2025
35 changes: 23 additions & 12 deletions lib/model/query/audits.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,19 @@ const getBySubmissionId = (submissionId, options) => ({ all }) => _getBySubmissi
// There is a separate query below to assemble full submission details for non-deleted
// submissions, but it was far too slow to have be part of this query.
const _getByEntityId = (fields, options, entityId) => sql`
SELECT ${fields} FROM entity_defs

LEFT JOIN entity_def_sources on entity_def_sources.id = entity_defs."sourceId"
INNER JOIN audits ON ((audits.details->>'entityDefId')::INTEGER = entity_defs.id OR (audits.details->>'sourceId')::INTEGER = entity_def_sources.id)

SELECT ${fields} FROM (
SELECT audits.* FROM audits
JOIN entities ON (audits.details->'entity'->>'uuid') = entities.uuid
WHERE entities.id = ${entityId}
UNION ALL
SELECT audits.* FROM audits
JOIN entity_def_sources ON (audits.details->>'sourceId')::INTEGER = entity_def_sources.id
JOIN entity_defs ON entity_def_sources.id = entity_defs."sourceId"
WHERE entity_defs."entityId" = ${entityId} AND audits.action = 'entity.bulk.create'
) audits

LEFT JOIN entity_defs ON (audits.details->>'entityDefId')::INTEGER = entity_defs.id
LEFT JOIN entity_def_sources ON entity_defs."sourceId" = entity_def_sources.id OR (audits.details->>'sourceId')::INTEGER = entity_def_sources.id
LEFT JOIN actors ON actors.id=audits."actorId"

LEFT JOIN audits triggering_event ON entity_def_sources."auditId" = triggering_event.id
Expand All @@ -165,7 +173,6 @@ SELECT ${fields} FROM entity_defs
LEFT JOIN audits submission_create_event ON (triggering_event.details->'submissionId')::INTEGER = (submission_create_event.details->'submissionId')::INTEGER AND submission_create_event.action = 'submission.create'
LEFT JOIN actors submission_create_event_actor ON submission_create_event_actor.id = submission_create_event."actorId"

WHERE entity_defs."entityId" = ${entityId}
ORDER BY audits."loggedAt" DESC, audits.id DESC
${page(options)}`;

Expand Down Expand Up @@ -253,13 +260,17 @@ const getByEntityId = (entityId, options) => ({ all }) => {

// Look up the full Submission information and attempt to merge it in if it exists.
const subOption = entityDefDict[audit.aux.def.id];
const fullSubmission = subOption.aux.submission
.map(s => s.withAux('submitter', subOption.aux.submissionActor.orNull()))
.map(s => s.withAux('currentVersion', subOption.aux.currentVersion.map(v => v.withAux('submitter', subOption.aux.currentSubmissionActor.orNull()))))
.map(s => s.forApi())
.map(s => mergeLeft(s, { xmlFormId: subOption.aux.form.map(f => f.xmlFormId).orNull() }));
let submission;

if (subOption) {
const fullSubmission = subOption.aux.submission
.map(s => s.withAux('submitter', subOption.aux.submissionActor.orNull()))
.map(s => s.withAux('currentVersion', subOption.aux.currentVersion.map(v => v.withAux('submitter', subOption.aux.currentSubmissionActor.orNull()))))
.map(s => s.forApi())
.map(s => mergeLeft(s, { xmlFormId: subOption.aux.form.map(f => f.xmlFormId).orNull() }));

const submission = mergeLeft(baseSubmission, fullSubmission.orElse(undefined));
submission = mergeLeft(baseSubmission, fullSubmission.orElse(undefined));
}

// Note: The source added to each audit event represents the source of the
// corresponding entity _version_, rather than the source of the event.
Expand Down
16 changes: 16 additions & 0 deletions test/integration/api/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,22 @@ describe('Entities API', () => {
logs[0].action.should.be.eql('entity.create');
});
}));

it('should return delete and restore events', testEntities(async (service) => {
const asAlice = await service.login('alice');

await asAlice.delete('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc')
.expect(200);

await asAlice.post('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc/restore')
.expect(200);

await asAlice.get('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc/audits')
.expect(200)
.then(({ body: logs }) => {
logs.map(l => l.action).should.be.eql(['entity.restore', 'entity.delete', 'entity.create']);
});
}));
});

describe('POST /datasets/:name/entities', () => {
Expand Down
Loading