From 94841b6226bf0d001a215784ee799bf4f4e05334 Mon Sep 17 00:00:00 2001 From: Kathleen Tuite Date: Thu, 18 Apr 2024 09:53:02 -0700 Subject: [PATCH] Better comments --- lib/model/query/audits.js | 82 ++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/lib/model/query/audits.js b/lib/model/query/audits.js index 018e56714..3a4d75e2d 100644 --- a/lib/model/query/audits.js +++ b/lib/model/query/audits.js @@ -131,7 +131,57 @@ const getBySubmissionId = (submissionId, options) => ({ all }) => _getBySubmissi return new Audit({ ...audit, details: { ...audit.details, entity } }, { actor: audit.aux.actor }); })); +// Get every audit for an entity. Some audits do not directly reference the +// entity or def, but instead reference a shared source, e.g. `entity.bulk.create` events. +// Fetching these events is done by looking at every Def of an Entity and the +// Source of that Def, then getting audits that reference either the Entity Def +// itself or the Source. +// +// This query also joins in other things linked from the Source, including: +// - The source's triggering event (e.g. an entity update event) and event actor +// - a submission creation event (and actor) IF a submission was part of the source. +// (This submission creation event is not necessarily the same as the triggering event +// but it can be.) +// +// This second part to get a submission creation event is to get basic information +// (mainly instanceId) about the source submission, even if it has been deleted. +// +// 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) + + LEFT JOIN actors ON actors.id=audits."actorId" + + LEFT JOIN audits triggering_event ON entity_def_sources."auditId" = triggering_event.id + LEFT JOIN actors triggering_event_actor ON triggering_event_actor.id = triggering_event."actorId" + -- if triggering event has a submissionId defined, look up creation event for that submission + -- it has info about the submission and creator we want to show even if the submission is deleted + 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)}`; + +// Get the full and current Submission linked to each Entity Def of a given Entity. +// If an Entity is created or updated by a Submission, that Entity Def is linked +// (via Entity Def Source) to the specific Submission Def that modified it. +// This query gets all relevant info about each Submission Def linked to each Def +// in an Entity. +// +// It can handle a variety of situations including: +// - The Def did not come from a Submission +// - The Submission has been purged, possibly by purging the whole Form +// - The Form has been soft-deleted so the Submission should not be returned +// - The Submission has been soft-deleted (though this is not possible to do) +// +// It will return the CURRENT version of the Dubmission rather than the version +// used to create the Entity Def (used to display updated Submission instanceName). const _getEntityDefsWithSubs = (fields, entityId) => sql` SELECT ${fields} FROM entity_defs LEFT JOIN entity_def_sources on entity_def_sources.id = entity_defs."sourceId" @@ -154,34 +204,10 @@ SELECT ${fields} FROM entity_defs LEFT JOIN actors submission_actor ON submission_actor.id = submissions."submitterId" LEFT JOIN actors current_submission_actor on current_submission_actor.id=current_submission_def."submitterId" - -- if some other kind of target object defined, add subquery here - -- ... WHERE entity_defs."entityId" = ${entityId} ORDER BY entity_defs.id DESC; `; -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) - - LEFT JOIN actors ON actors.id=audits."actorId" - - LEFT JOIN audits triggering_event ON entity_def_sources."auditId" = triggering_event.id - LEFT JOIN actors triggering_event_actor ON triggering_event_actor.id = triggering_event."actorId" - - -- if triggering event has a submissionId defined, look up creation event for that submission - -- it has info about the submission and creator we want to show even if the submission is deleted - 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" - - -- if some other kind of target object defined, add subquery here - -- ... - WHERE entity_defs."entityId" = ${entityId} - ORDER BY audits."loggedAt" DESC, audits.id DESC - ${page(options)}`; - const getByEntityId = (entityId, options) => ({ all }) => { const _unjoiner = unjoiner( @@ -202,8 +228,10 @@ const getByEntityId = (entityId, options) => ({ all }) => { all(_getByEntityId(_unjoiner.fields, options, entityId)).then(map(_unjoiner)), all(_getEntityDefsWithSubs(_defUnjoiner.fields, entityId)).then(map(_defUnjoiner)) ]) - .then(([audits, defs]) => { - const entityDefDict = Object.fromEntries(defs.map(def => [def.id, def])); + .then(([audits, defsWithSubs]) => { + // Build a map of Entity Def Ids to objects that contain full Submission information + // linked to that Def (if Submission exists and if Def is even linked to a Submission). + const entityDefDict = Object.fromEntries(defsWithSubs.map(def => [def.id, def])); return audits.map(audit => { const entitySourceDetails = audit.aux.source.forApi(); @@ -222,7 +250,7 @@ const getByEntityId = (entityId, options) => ({ all }) => { })) .orElse(undefined); - // Looking up in a different dict for the full submission details if they exist + // 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()))