Skip to content

Commit

Permalink
update entity from submission
Browse files Browse the repository at this point in the history
  • Loading branch information
ktuite committed Sep 27, 2023
1 parent 89c6b6c commit 9b3acdf
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 20 deletions.
2 changes: 2 additions & 0 deletions lib/data/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const parseSubmissionXml = (entityFields, xml) => new Promise((resolve, reject)
entity.system.dataset = field.attrs.dataset;
entity.system.id = field.attrs.id;
entity.system.create = field.attrs.create;
entity.system.update = field.attrs.update; // TODO: add tests
entity.system.baseVersion = field.attrs.baseVersion; // TODO: add tests
} else if (field.path.indexOf('/meta/entity') === 0)
entity.system[field.name] = text;
else if (field.propertyName != null)
Expand Down
73 changes: 54 additions & 19 deletions lib/model/query/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
const { sql } = require('slonik');
const { Actor, Entity, Submission, Form } = require('../frames');
const { equals, extender, unjoiner, page, markDeleted } = require('../../util/db');
const { map } = require('ramda');
const { map, mergeRight } = require('ramda');
const { blankStringToNull, construct } = require('../../util/util');
const { QueryOptions } = require('../../util/db');
const { odataFilter } = require('../../data/odata-filter');
Expand Down Expand Up @@ -89,9 +89,53 @@ const createSource = (details = null, subDefId = null, eventId = null) => ({ one
.then((row) => row.id);
};

//
// Entity event processing
//

const _createEntity = (dataset, entityData, submissionId, submissionDef, submissionDefId, event, parentEvent) => async ({ Entities, Audits }) => {
const partial = await Entity.fromParseEntityData(entityData);

const sourceDetails = { submission: { instanceId: submissionDef.instanceId }, parentEventId: parentEvent ? parentEvent.id : undefined };
const sourceId = await Entities.createSource(sourceDetails, submissionDefId, event.id);
const entity = await Entities.createNew(dataset, partial, submissionDef, sourceId);

return Audits.log({ id: event.actorId }, 'entity.create', { acteeId: dataset.acteeId },
{
entityId: entity.id, // Added in v2023.3 and backfilled
entityDefId: entity.aux.currentVersion.id, // Added in v2023.3 and backfilled
entity: { uuid: entity.uuid, dataset: dataset.name },
submissionId,
submissionDefId
});
};

const _updateEntity = (dataset, entityData, submissionId, submissionDef, submissionDefId, event, parentEvent) => async ({ Entities }) => {

// Get version of entity on the server
const serverEntity = await Entities.getById(dataset.id, entityData.system.id).then(o => o.get());

// merge data
const mergedData = mergeRight(serverEntity.aux.currentVersion.data, entityData.data);
const mergedLabel = entityData.system.label || serverEntity.aux.currentVersion.label;

// make some kind of source object
const sourceDetails = {
submission: { instanceId: submissionDef.instanceId },
dataParsedFromSubmission: entityData.data, // or some other info
labelParsedFromSubmission: entityData.label,
baseVersionParsedFromSubmission: 0,
parentEventId: parentEvent ? parentEvent.id : undefined,
};
const sourceId = await Entities.createSource(sourceDetails, submissionDefId, event.id);

// TODO: createVersion infers creator id from context.auth, shoving the submitter id in as another arg
// should be refactored
return Entities.createVersion(dataset, serverEntity, mergedData, mergedLabel, serverEntity.aux.currentVersion.version + 1, sourceId, null, submissionDef.submitterId);
};

// Entrypoint to where submissions (a specific version) become entities
const _processSubmissionEvent = (event, parentEvent) => async ({ Datasets, Entities, Submissions, Forms, Audits }) => {
const _processSubmissionEvent = (event, parentEvent) => async ({ Datasets, Entities, Submissions, Forms }) => {
const { submissionId, submissionDefId } = event.details;

const form = await Forms.getByActeeId(event.acteeId);
Expand Down Expand Up @@ -145,23 +189,13 @@ const _processSubmissionEvent = (event, parentEvent) => async ({ Datasets, Entit
return null;

// If create is not true (either 1 or true) then we don't need to process further
if (!(entityData.system.create === '1' || entityData.system.create === 'true'))
return null;
if (entityData.system.create === '1' || entityData.system.create === 'true')
return Entities._createEntity(dataset, entityData, submissionId, submissionDef, submissionDefId, event, parentEvent);

const partial = await Entity.fromParseEntityData(entityData);

const sourceDetails = { submission: { instanceId: submissionDef.instanceId }, parentEventId: parentEvent ? parentEvent.id : undefined };
const sourceId = await Entities.createSource(sourceDetails, submissionDefId, event.id);
const entity = await Entities.createNew(dataset, partial, submissionDef, sourceId);
if (entityData.system.update === '1' || entityData.system.update === 'true')
return Entities._updateEntity(dataset, entityData, submissionId, submissionDef, submissionDefId, event, parentEvent);

return Audits.log({ id: event.actorId }, 'entity.create', { acteeId: dataset.acteeId },
{
entityId: entity.id, // Added in v2023.3 and backfilled
entityDefId: entity.aux.currentVersion.id, // Added in v2023.3 and backfilled
entity: { uuid: entity.uuid, dataset: dataset.name },
submissionId,
submissionDefId
});
return null;
};

const processSubmissionEvent = (event, parentEvent) => (container) =>
Expand Down Expand Up @@ -195,9 +229,9 @@ const createEntitiesFromPendingSubmissions = (submissionEvents, parentEvent) =>
////////////////////////////////////////////////////////////////////////////////
// UPDATING ENTITIES

const createVersion = (dataset, entity, data, label, version, sourceId, userAgentIn = null) => ({ context, one }) => {
const createVersion = (dataset, entity, data, label, version, sourceId, userAgentIn = null, submitterId = null) => ({ context, one }) => {
// dataset is passed in so the audit log can use its actee id
const creatorId = context.auth.actor.map((actor) => actor.id).orNull();
const creatorId = (context.auth ? context.auth.actor.map((actor) => actor.id).orNull() : submitterId);
const userAgent = blankStringToNull(userAgentIn);
const json = JSON.stringify(data);

Expand Down Expand Up @@ -364,6 +398,7 @@ del.audit = (entity, dataset) => (log) => log('entity.delete', entity.with({ act
module.exports = {
createNew, _processSubmissionEvent,
createSource,
_createEntity, _updateEntity,
processSubmissionEvent, streamForExport,
getDefBySubmissionId,
createVersion,
Expand Down
57 changes: 57 additions & 0 deletions test/integration/worker/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -808,5 +808,62 @@ describe('worker: entity', () => {
});
}));
});

describe('should update an entity', () => {
it('should update 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)
.set('Content-Type', 'application/xml')
.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.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.updateEntity)
.set('Content-Type', 'application/xml')
.expect(200);

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

await exhaust(container);

await asAlice.get('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc')
.expect(200)
.then(({ body: person, headers }) => {
person.currentVersion.data.should.eql({ age: '85', first_name: 'Alicia' });
person.currentVersion.label.should.eql('Alicia (85)');
headers.etag.should.be.eql('"2"');
});

// update again
await asAlice.post('/v1/projects/1/forms/updateEntity/submissions')
.send(testData.instances.updateEntity.one
.replace('<instanceID>one</instanceID>', '<instanceID>one-v2</instanceID>')
.replace('<age>85</age>', '<age>84</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')
.set('X-Extended-Metadata', true)
.expect(200)
.then(({ body: person, headers }) => {
person.currentVersion.data.should.eql({ age: '84', first_name: 'Alicia' });
person.currentVersion.label.should.eql('Alicia (85)');
headers.etag.should.be.eql('"3"');
});
}));
});
});

4 changes: 3 additions & 1 deletion test/unit/data/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ describe('extracting entities from submissions', () => {
create: '1',
id: 'uuid:12345678-1234-4123-8234-123456789abc',
label: 'Alice (88)',
dataset: 'people'
dataset: 'people',
update: undefined,
baseVersion: undefined
}));
}));

Expand Down

0 comments on commit 9b3acdf

Please sign in to comment.