Skip to content

Commit ab0f6be

Browse files
committed
new: API entities/:uuid/versions
1 parent 376e2bb commit ab0f6be

File tree

3 files changed

+81
-26
lines changed

3 files changed

+81
-26
lines changed

lib/model/query/entities.js

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,47 @@ const getAll = (datasetId, options = QueryOptions.none) => ({ all }) =>
173173
_get(false)(all, options.withCondition({ datasetId }), isTrue(options.argData.deleted))
174174
.then(map((e) => e.withAux('currentVersion', e.aux.currentVersion.withAux('creator', e.aux.currentVersionCreator))));
175175

176+
const getByUuid = (uuid) => ({ maybeOne }) =>
177+
maybeOne(sql`select * from entities where "uuid" = ${uuid} limit 1`)
178+
.then(map(construct(Entity)));
179+
180+
////////////////////////////////////////////////////////////////////////////////
181+
// GETTING ENTITY DEFS
182+
183+
const _getDef = extender(Entity.Def, Submission, Submission.Def.into('submissionDef'), Form)(Actor.into('creator'))((fields, extend, options) => sql`
184+
SELECT ${fields} FROM entities
185+
JOIN entity_defs ON entities.id = entity_defs."entityId"
186+
LEFT JOIN submission_defs ON submission_defs.id = entity_defs."submissionDefId"
187+
LEFT JOIN (
188+
SELECT submissions.*, submission_defs."userAgent" FROM submissions
189+
JOIN submission_defs ON submissions.id = submission_defs."submissionId" AND root
190+
) submissions ON submissions.id = submission_defs."submissionId"
191+
LEFT JOIN forms ON submissions."formId" = forms.id
192+
${extend||sql`
193+
LEFT JOIN actors ON actors.id=entities."creatorId"
194+
LEFT JOIN actors current_version_actors ON current_version_actors.id=entity_defs."creatorId"
195+
`}
196+
where ${equals(options.condition)} AND entities."deletedAt" IS NULL
197+
order by entity_defs."createdAt", entity_defs.id
198+
`);
199+
200+
const getAllDefs = (datasetId, uuid, options = QueryOptions.none) => ({ all }) =>
201+
_getDef(all, options.withCondition({ datasetId, uuid }))
202+
.then(map((v) => {
203+
const isSourceSubmission = !!v.submissionDefId;
204+
205+
// TODO: revisit this when working on POST /entities
206+
const source = new Entity.Def.Source({
207+
type: isSourceSubmission ? 'submission' : 'api',
208+
details: isSourceSubmission ? {
209+
xmlFormId: v.aux.form.xmlFormId,
210+
instanceId: v.aux.submission.instanceId,
211+
instanceName: v.aux.submissionDef.instanceName
212+
} : null
213+
});
214+
215+
return new Entity.Def(v, { creator: v.aux.creator, source });
216+
}));
176217

177218
// This will check for an entity related to any def of the same submission
178219
// as the one specified. Used when trying to reapprove an edited submission.
@@ -183,9 +224,6 @@ const getDefBySubmissionId = (submissionId) => ({ maybeOne }) =>
183224
where s.id = ${submissionId} limit 1`)
184225
.then(map(construct(Entity.Def)));
185226

186-
const getByUuid = (uuid) => ({ maybeOne }) =>
187-
maybeOne(sql`select * from entities where "uuid" = ${uuid} limit 1`)
188-
.then(map(construct(Entity)));
189227

190228

191229
////////////////////////////////////////////////////////////////////////////////
@@ -217,5 +255,5 @@ module.exports = {
217255
processSubmissionEvent, streamForExport,
218256
getDefBySubmissionId, getByUuid,
219257
countByDatasetId, getById,
220-
getAll
258+
getAll, getAllDefs
221259
};

lib/resources/entities.js

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
// including this file, may be copied, modified, propagated, or distributed
88
// except according to the terms contained in the LICENSE file.
99

10-
const { getOrNotFound } = require('../util/promise');
10+
const { getOrNotFound, reject } = require('../util/promise');
1111
const { mergeLeft } = require('ramda');
1212
const { success } = require('../util/http');
13+
const Problem = require('../util/problem');
1314

1415
const getActor = () => ({
1516
id: 1,
@@ -56,28 +57,18 @@ module.exports = (service, endpoint) => {
5657
return Entities.getById(dataset.id, params.uuid, queryOptions).then(getOrNotFound);
5758
}));
5859

59-
service.get('/projects/:id/datasets/:name/entities/:uuid/versions', endpoint(async ({ Projects }, { params, queryOptions }) => {
60+
service.get('/projects/:projectId/datasets/:name/entities/:uuid/versions', endpoint(async ({ Datasets, Entities }, { params, auth, queryOptions }) => {
6061

61-
await Projects.getById(params.id, queryOptions).then(getOrNotFound);
62+
const dataset = await Datasets.get(params.projectId, params.name, true).then(getOrNotFound);
6263

63-
const versions = ['John Doe', 'Jane Doe', 'Richard Roe', 'Janie Doe', 'Baby Roe'].map((name, i) => ({
64-
...getEntityDef(),
65-
current: i === 4,
66-
source: {
67-
type: 'api',
68-
details: null
69-
},
70-
data: {
71-
firstName: name.split(' ')[0],
72-
lastName: name.split(' ')[1],
73-
city: 'Toronto'
74-
},
75-
versionNumber: i+1,
76-
label: name,
77-
createdAt: new Date()
78-
}));
64+
await auth.canOrReject('entity.read', dataset);
65+
66+
const defs = await Entities.getAllDefs(dataset.id, params.uuid, queryOptions);
67+
68+
// it means there's no entity with the provided UUID
69+
if (defs.length === 0) return reject(Problem.user.notFound());
7970

80-
return versions;
71+
return defs;
8172
}));
8273

8374
// May not be needed for now

test/integration/api/entities.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,36 @@ describe('Entities API', () => {
201201
});
202202

203203
describe('GET /datasets/:name/entities/:uuid/versions', () => {
204-
it('should return all versions of the Entity', testService(async (service) => {
204+
it('should return notfound if the dataset does not exist', testEntities(async (service) => {
205+
const asAlice = await service.login('alice');
206+
207+
await asAlice.get('/v1/projects/1/datasets/nonexistent/entities/123/versions')
208+
.expect(404);
209+
}));
210+
211+
it('should return notfound if the entity does not exist', testEntities(async (service) => {
212+
const asAlice = await service.login('alice');
213+
214+
await asAlice.get('/v1/projects/1/datasets/people/entities/123/versions')
215+
.expect(404);
216+
}));
217+
218+
it('should reject if the user cannot read', testEntities(async (service) => {
219+
const asChelsea = await service.login('chelsea');
220+
221+
await asChelsea.get('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc')
222+
.expect(403);
223+
}));
224+
225+
it('should return all versions of the Entity', testEntities(async (service, container) => {
205226
const asAlice = await service.login('alice');
206227

207-
await asAlice.get('/v1/projects/1/datasets/People/entities/00000000-0000-0000-0000-000000000001/versions')
228+
await container.db.any(sql`UPDATE entity_defs SET current = false;`);
229+
await container.db.any(sql`
230+
INSERT INTO entity_defs ("entityId", "createdAt", "current", "submissionDefId", "data", "creatorId", "userAgent", "label")
231+
SELECT "entityId", clock_timestamp(), TRUE, NULL, "data", "creatorId", 'postman', "label" || ' updated' FROM entity_defs`);
232+
233+
await asAlice.get('/v1/projects/1/datasets/people/entities/12345678-1234-4123-8234-123456789abc/versions')
208234
.expect(200)
209235
.then(({ body: versions }) => {
210236
versions.forEach(v => {

0 commit comments

Comments
 (0)