Skip to content

Commit

Permalink
Fetch entity info from db instead of audit details
Browse files Browse the repository at this point in the history
  • Loading branch information
ktuite committed Aug 23, 2023
1 parent 841f215 commit 4c77a42
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 6 deletions.
2 changes: 1 addition & 1 deletion lib/model/frames/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const { extractEntity, validateEntity } = require('../../data/entity');

// These Frames don't interact with APIs directly, hence no readable/writable
class Entity extends Frame.define(
table('entities'),
table('entities', 'entity'),
'id', 'uuid', readable,
'datasetId',
'createdAt', readable, 'creatorId', readable,
Expand Down
47 changes: 45 additions & 2 deletions lib/model/query/audits.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

const { sql } = require('slonik');
const { map, mergeLeft } = require('ramda');
const { Actee, Actor, Audit, Dataset, Form, Project, Submission } = require('../frames');
const { Actee, Actor, Audit, Dataset, Entity, Form, Project, Submission } = require('../frames');
const { extender, equals, page, QueryOptions, unjoiner } = require('../../util/db');
const Option = require('../../util/option');
const { construct } = require('../../util/util');
Expand Down Expand Up @@ -99,7 +99,7 @@ const get = (options = QueryOptions.none) => ({ all }) =>
return new Audit(row, { actor: row.aux.actor, actee: Option.firstDefined(actees) });
});
});

/*
const _getBySubmissionId = extender(Audit)(Option.of(Actor))((fields, extend, options, submissionId) => sql`
select ${fields} from audits
${extend|| sql`left outer join actors on actors.id=audits."actorId"`}
Expand All @@ -108,7 +108,50 @@ select ${fields} from audits
${page(options)}`);
const getBySubmissionId = (submissionId, options) => ({ all }) =>
_getBySubmissionId(all, options, submissionId);
*/

// TODO: bring back extender???
const _getBySubmissionId = (fields, options, submissionId) => sql`
SELECT ${fields} FROM audits
left outer join actors on actors.id=audits."actorId"
-- if event has entity uuid in details
-- TOOD: this isn't the current entity def, it's the linked def...
LEFT JOIN entities ON (audits.details->'entityId')::INTEGER = entities.id
LEFT JOIN entity_defs AS current_entity_def ON (audits.details->'entityDefId')::INTEGER = current_entity_def.id
LEFT JOIN datasets ON entities."datasetId" = datasets.id
WHERE (audits.details->>'submissionId')::INTEGER = ${submissionId}
ORDER BY audits."loggedAt" DESC, audits.id DESC
${page(options)}`;

const getBySubmissionId = (entityId, options) => ({ all }) => {
const _unjoiner = unjoiner(
Audit, Actor,
Option.of(Entity), Option.of(Entity.Def.alias('current_entity_def', 'currentVersion')),
Option.of(Dataset)
);
return all(_getBySubmissionId(_unjoiner.fields, options, entityId))
.then(map(_unjoiner))
.then(map(audit => {

const entity = audit.aux.entity
.map(s => s.withAux('currentVersion', audit.aux.currentVersion))
.map(s => s.forApi());
const dataset = audit.aux.dataset
.map(s => s.forApi());

const details = mergeLeft(audit.details, {
dataset: dataset.orElse(undefined)
});

if (entity.isDefined())
details.entity = entity.get();

return new Audit({ ...audit, details }, { actor: audit.aux.actor });
}));
};


const _getByEntityId = (fields, options, entityId) => sql`
Expand Down
4 changes: 2 additions & 2 deletions lib/model/query/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ createNew.audit = (newEntity, dataset, partial, subDef) => (log) => {
return log('entity.create', dataset, {
entityId: newEntity.id, // Added in v2023.3 and backfilled
entityDefId: newEntity.aux.currentVersion.id, // Added in v2023.3 and backfilled
entity: { uuid: newEntity.uuid, label: newEntity.aux.currentVersion.label, dataset: dataset.name }
entity: { uuid: newEntity.uuid, dataset: dataset.name } // label removed in 2023.4
});
};
createNew.audit.withResult = true;
Expand Down Expand Up @@ -158,7 +158,7 @@ const _processSubmissionEvent = (event, parentEvent) => async ({ Datasets, Entit
{
entityId: entity.id, // Added in v2023.3 and backfilled
entityDefId: entity.aux.currentVersion.id, // Added in v2023.3 and backfilled
entity: { uuid: entity.uuid, label: entity.aux.currentVersion.label, dataset: dataset.name },
entity: { uuid: entity.uuid, dataset: dataset.name }, // label removed in 2023.4
submissionId,
submissionDefId
});
Expand Down
103 changes: 103 additions & 0 deletions test/integration/api/submissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3012,6 +3012,22 @@ one,h,/data/h,2000-01-01T00:06,2000-01-01T00:07,-5,-6,,ee,ff
body[2].details.should.eql({ instanceId: 'one', submissionId, submissionDefId: submissionDefIds[1] });
}))))));

it.skip('should not expand actor when not extended', testService((service) =>
service.login('alice', (asAlice) =>
asAlice.post('/v1/projects/1/forms/simple/submissions')
.send(testData.instances.simple.one)
.set('Content-Type', 'text/xml')
.expect(200)
.then(() => asAlice.patch('/v1/projects/1/forms/simple/submissions/one')
.send({ reviewState: 'rejected' })
.expect(200))
.then(() => asAlice.get('/v1/projects/1/forms/simple/submissions/one/audits')
.expect(200)
.then(({ body }) => {
should.not.exist(body[0].actor);
body[0].actorId.should.equal(5);
})))));

it('should expand actor on extended', testService((service) =>
service.login('alice', (asAlice) =>
asAlice.post('/v1/projects/1/forms/simple/submissions')
Expand All @@ -3028,6 +3044,93 @@ one,h,/data/h,2000-01-01T00:06,2000-01-01T00:07,-5,-6,,ee,ff
body[0].actor.should.be.an.Actor();
body[0].actor.displayName.should.equal('Alice');
})))));

describe('submission audits about entity events', () => {
it('should return full entity in details of an event about an entity', testService(async (service, container) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.expect(200);

await asAlice.post('/v1/projects/1/forms/simpleEntity/submissions')
.send(testData.instances.simpleEntity.one)
.set('Content-Type', 'application/xml')
.expect(200);

await exhaust(container);

await asAlice.get('/v1/projects/1/forms/simpleEntity/submissions/one/audits')
.set('X-Extended-Metadata', true)
.expect(200)
.then(({ body }) => {
const entityCreate = body[0];
entityCreate.details.entity.uuid.should.equal('12345678-1234-4123-8234-123456789abc');
entityCreate.details.entity.should.be.an.Entity();
entityCreate.details.entity.currentVersion.should.be.an.EntityDef();
entityCreate.details.dataset.should.be.a.Dataset();
});
}));

it('should return entity uuid and dataset when entity is soft-deleted', testService(async (service, container) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.expect(200);

await asAlice.post('/v1/projects/1/forms/simpleEntity/submissions')
.send(testData.instances.simpleEntity.one)
.set('Content-Type', 'application/xml')
.expect(200);

await exhaust(container);

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

// TODO: this case probably shouldn't have the entity label and stuff
await asAlice.get('/v1/projects/1/forms/simpleEntity/submissions/one/audits')
.set('X-Extended-Metadata', true)
.expect(200)
.then(({ body }) => {
const entityCreate = body[0];
entityCreate.details.entity.uuid.should.equal('12345678-1234-4123-8234-123456789abc');
entityCreate.details.entity.should.be.an.Entity();
entityCreate.details.entity.currentVersion.should.be.an.EntityDef();
entityCreate.details.dataset.should.be.a.Dataset();
});
}));

it('should return entity uuid and dataset when entity is (manually) purged', testService(async (service, container) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.expect(200);

await asAlice.post('/v1/projects/1/forms/simpleEntity/submissions')
.send(testData.instances.simpleEntity.one)
.set('Content-Type', 'application/xml')
.expect(200);

await exhaust(container);

// cascade on delete isn't set up for entitites so let's ruthlessly delete all the entities
await container.run(sql`delete from entity_defs`);
await container.run(sql`delete from entities`);

// TODO: this case probably shouldn't have the entity label and stuff
await asAlice.get('/v1/projects/1/forms/simpleEntity/submissions/one/audits')
.set('X-Extended-Metadata', true)
.expect(200)
.then(({ body }) => {
const entityCreate = body[0];
entityCreate.details.entity.uuid.should.equal('12345678-1234-4123-8234-123456789abc');
entityCreate.details.entity.dataset.should.equal('people');
entityCreate.details.entity.should.not.be.an.Entity();
});
}));
});
});

describe('/:instanceId.xml GET', () => {
Expand Down
1 change: 0 additions & 1 deletion test/integration/worker/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ describe('worker: entity', () => {
createEvent.details.submissionId.should.equal(updateEvent.details.submissionId);

// should contain information about entity
createEvent.details.entity.label.should.equal('Alice (88)');
createEvent.details.entity.dataset.should.equal('people');
createEvent.details.entity.uuid.should.equal('12345678-1234-4123-8234-123456789abc');

Expand Down

0 comments on commit 4c77a42

Please sign in to comment.