Skip to content

Commit

Permalink
Handle dataset name capitalization conflicts in API
Browse files Browse the repository at this point in the history
  • Loading branch information
ktuite committed Apr 19, 2024
1 parent c9bae80 commit 31e0f2e
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 15 deletions.
20 changes: 19 additions & 1 deletion lib/model/query/datasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,25 @@ const createOrMerge = (parsedDataset, form, fields) => async ({ one, Actees, Dat
};

// Insert a Dataset when there is no associated form and immediately publish
const createPublishedDataset = (dataset, project) => async ({ one, Actees }) => {
const createPublishedDataset = (dataset, project) => async ({ one, Actees, Datasets }) => {

// Check for existing dataset with the same name
const existingDataset = await Datasets.get(project.id, dataset.name, false);
if (existingDataset.isDefined()) {
const { publishedAt, id: datasetId } = existingDataset.get();
if (publishedAt)
throw Problem.user.uniquenessViolation({ table: 'datasets', fields: [ 'name', 'projectId' ], values: [ dataset.name, project.id ], });

// If existing dataset is only a draft, allow it to be published
return new Dataset(await one(sql`UPDATE datasets SET "publishedAt" = "createdAt" where id = ${datasetId} RETURNING *`));
}

// Check for a published dataset with a similar name but different capitalization
const conflictingName = await Datasets.getPublishedBySimilarName(project.id, dataset.name);
if (conflictingName.isDefined())
throw Problem.user.datasetNameConflict({ current: conflictingName.get().name, provided: dataset.name });

// Final case: publish a completely new dataset
const actee = await Actees.provision('dataset', project);
const dsWithId = await one(insert(dataset.with({ acteeId: actee.id })));
return new Dataset(await one(sql`UPDATE datasets SET "publishedAt" = "createdAt" where id = ${dsWithId.id} RETURNING *`));
Expand Down
87 changes: 73 additions & 14 deletions test/integration/api/datasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,22 +98,81 @@ describe('datasets and entities', () => {
});
}));

it('should reject if creating a dataset that already exists', testService(async (service) => {
const asAlice = await service.login('alice');
describe('dataset name conflicts', () => {
it('should reject if creating a dataset that already exists', testService(async (service) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'trees'
})
.expect(200);
await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'trees'
})
.expect(200);

// Second time
await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'trees'
})
.expect(409);
}));
// Second time
await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'trees'
})
.expect(409)
.then(({ body }) => {
body.code.should.equal(409.3);
body.message.should.startWith('A resource already exists with name,projectId value(s) of trees,');
});
}));

it('should reject if creating a dataset that has a similar name to an existing published dataset', testService(async (service) => {
const asAlice = await service.login('alice');

await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'trees'
})
.expect(200);

// Second time
await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'TREES'
})
.expect(409)
.then(({ body }) => {
body.code.should.equal(409.16);
body.message.should.startWith("A dataset named 'trees' exists and you provided 'TREES'");
});
}));

it('should allow creating a dataset that only exists as a draft', testService(async (service) => {
const asAlice = await service.login('alice');

// draft "people" dataset
await asAlice.post('/v1/projects/1/forms')
.send(testData.forms.simpleEntity)
.set('Content-Type', 'application/xml')
.expect(200);

await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'people'
})
.expect(200);
}));

it('should allow creating a dataset that has a similar name to a draft dataset', testService(async (service) => {
const asAlice = await service.login('alice');

// draft "people" dataset
await asAlice.post('/v1/projects/1/forms')
.send(testData.forms.simpleEntity)
.set('Content-Type', 'application/xml')
.expect(200);

await asAlice.post('/v1/projects/1/datasets')
.send({
name: 'PEOPLE'
})
.expect(200);
}));
});

it('should add label-only entity to dataset, all via API', testService(async (service) => {
const asAlice = await service.login('alice');
Expand Down

0 comments on commit 31e0f2e

Please sign in to comment.