Skip to content

Commit

Permalink
Generate entity UUID if one is not provided in API request (#1086)
Browse files Browse the repository at this point in the history
* Generate entity UUID if one is not provided in API request

* Updated docs to mention optional uuid in entity create
  • Loading branch information
ktuite authored Feb 13, 2024
1 parent c1cd73f commit 90d4070
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
10 changes: 9 additions & 1 deletion docs/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ info:
- OData Data Document for requests of Submissions and Entities now allow use of `$orderby`.
- ETag headers on all Blobs.

**Changed**:

- The [Entity Create](/central-api-entity-management/#creating-an-entity) endpoint will now generate a UUID if the `uuid` parameter is not provided.


## ODK Central v2023.5

Expand Down Expand Up @@ -10068,7 +10072,11 @@ paths:
- Entity Management
summary: Creating an Entity
description: |-
Creates an Entity in the Dataset. Request body takes the JSON representation of the Entity. It should have `uuid` and `label` property in addition to the user-defined properties of the Dataset in `data` property.
Creates an Entity in the Dataset. The request body takes a JSON representation of the Entity, which has the following properties:

1. A `data` object containing values for the user-defined Dataset properties. (Not all properties have to have values.)
2. A `label` property, which cannot be blank or an empty string. (This is used as a human-readable label in Forms that consume Entities.)
3. An optional `uuid` property. If the `uuid` is not specified, Central will generate a UUID for an Entity with the provided data and label.

Value type of all properties is `string`.

Expand Down
3 changes: 2 additions & 1 deletion lib/data/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
const csv = require('csv-stringify');
const { clone, path, mergeLeft } = require('ramda');
const { Transform } = require('stream');
const uuid = require('uuid').v4;
const { PartialPipe } = require('../util/stream');
const Problem = require('../util/problem');
const { submissionXmlToFieldStream } = require('./submission');
Expand Down Expand Up @@ -112,7 +113,7 @@ const extractEntity = (body, propertyNames, existingEntity) => {
if (body.uuid && typeof body.uuid !== 'string')
throw Problem.user.invalidDataTypeOfParameter({ field: 'uuid', expected: 'string' });

entity.system.uuid = normalizeUuid(body.uuid);
entity.system.uuid = (body.uuid) ? normalizeUuid(body.uuid) : uuid();
}

if (body.label != null)
Expand Down
51 changes: 51 additions & 0 deletions test/integration/api/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,36 @@ describe('Entities API', () => {
});
}));

it('should generate uuid if one is not provided', testDataset(async (service) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/datasets/people/entities')
.send({
label: 'Johnny Doe',
data: {
first_name: 'Johnny',
age: '22'
}
})
.expect(200)
.then(({ body: person }) => {
person.should.be.an.Entity();
person.uuid.should.be.a.uuid();
person.creatorId.should.equal(5);
person.should.have.property('currentVersion').which.is.an.EntityDef();
person.currentVersion.should.have.property('label').which.equals('Johnny Doe');
person.currentVersion.should.have.property('data').which.is.eql({
first_name: 'Johnny',
age: '22'
});
person.currentVersion.should.have.property('dataReceived').which.is.eql({
first_name: 'Johnny',
age: '22',
label: 'Johnny Doe'
});
});
}));

it('should reject if uuid is not a valid uuid', testDataset(async (service) => {
const asAlice = await service.login('alice');

Expand Down Expand Up @@ -2095,6 +2125,27 @@ describe('Entities API', () => {
logs[0].details.errorMessage.should.equal('Required parameter label missing.');
});
}));

it('should not create entity if the uuid is missing in the submission', testDataset(async (service, container) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/forms/simpleEntity/submissions')
.send(testData.instances.simpleEntity.one
.replace('id="uuid:12345678-1234-4123-8234-123456789abc"', 'id=""'))
.set('Content-Type', 'application/xml')
.expect(200);

await exhaust(container);

await asAlice.get('/v1/projects/1/forms/simpleEntity/submissions/one/audits')
.expect(200)
.then(({ body: logs }) => {
logs[0].should.be.an.Audit();
logs[0].action.should.be.eql('entity.error');
logs[0].details.problem.problemCode.should.equal(400.2);
logs[0].details.errorMessage.should.equal('Required parameter uuid missing.');
});
}));
});

describe('entity updates from submissions', () => {
Expand Down

0 comments on commit 90d4070

Please sign in to comment.