Skip to content

Commit

Permalink
Merge pull request #2499 from Inist-CNRS/feat/2330-delete-annotation-api
Browse files Browse the repository at this point in the history
Feat(annotation): Add annotation deletion support
  • Loading branch information
jonathanarnault authored Feb 7, 2025
2 parents 7837de2 + 2fa4676 commit 4bc97b1
Show file tree
Hide file tree
Showing 13 changed files with 593 additions and 38 deletions.
4 changes: 4 additions & 0 deletions cypress/e2e/phase_1/admin_display.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ describe('Home Page', () => {
cy.findByRole('gridcell', {
name: /newField/,
timeout: 1000,
}).trigger('mouseenter');

cy.findByRole('button', {
name: /edit-newField/,
}).click();

cy.get(`button[value="home"]`).should('not.have.class', 'Mui-selected');
Expand Down
125 changes: 122 additions & 3 deletions cypress/e2e/phase_4/annotation.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe('Annotation', () => {

describe('resources', () => {
describe('create list and update an Annotation', () => {
it.only('should create an annotation on resource field', () => {
it('should create an annotation on resource field', () => {
cy.findByText('Search').click();
searchDrawer.search('Terminator 2');
searchDrawer.waitForLoading();
Expand Down Expand Up @@ -181,7 +181,7 @@ describe('Annotation', () => {
'Internal Comment',
'Administrator',
'Contributor',
'Comment',
'Contributor Comment',
'Submission date',
'Last modified on',
]);
Expand Down Expand Up @@ -284,7 +284,9 @@ describe('Annotation', () => {
'exist',
);

cy.findByText('Annotations').click();
cy.findByRole('link', {
name: 'Cancel',
}).click();

cy.wait(1000);
cy.findByRole('progressbar').should('not.exist');
Expand Down Expand Up @@ -336,6 +338,123 @@ describe('Annotation', () => {
name: `Add an annotation to Liste des films field`,
}).should('not.exist');
});

it('should delete an annotation on resource field', () => {
cy.findByText('Search').click();
searchDrawer.search('Terminator 2');
searchDrawer.waitForLoading();
cy.findByTitle('Terminator 2').click();
annotation.createTitleAnnotation({
fieldLabel: 'actors',
comment: 'This is a comment',
authorName: 'John Doe',
authorEmail: '[email protected]',
});

cy.findByText('Search').click();
searchDrawer.search('RoboCop');
searchDrawer.waitForLoading();
cy.findByTitle('RoboCop').click();
annotation.createTitleAnnotation({
fieldLabel: 'rating',
comment: 'This is another comment',
authorName: 'Jane Smith',
authorEmail: '[email protected]',
});
cy.findByText('More').click();
menu.goToAdminDashboard();
cy.findByText('Annotations').click();

cy.findAllByRole('cell').then((cells) => {
const firstUri = cells[0].textContent;
const secondUri = cells[14].textContent;

expect(firstUri).to.match(/uid:\//);
expect(secondUri).to.match(/uid:\//);

expect(
cells.toArray().map((cell) => cell.textContent),
).to.deep.equal([
firstUri,
'RoboCop',
'rating',
'[bZE+]',
'',
'',
'',
'To Review',
'',
'',
'Jane Smith',
'This is another comment',
new Date().toLocaleDateString(),
new Date().toLocaleDateString(),
secondUri,
'Terminator 2',
'actors',
'[K8Lu]',
'',
'',
'',
'To Review',
'',
'',
'John Doe',
'This is a comment',
new Date().toLocaleDateString(),
new Date().toLocaleDateString(),
]);
});

cy.findByText('Terminator 2').click();

cy.findByRole('heading', {
name: /^Annotation: uid:\//,
}).should('be.visible');
cy.findByRole('heading', {
name: 'Terminator 2',
}).should('be.visible');

cy.findByRole('button', {
name: 'Delete the annotation',
}).click();

cy.findByRole('button', {
name: 'Delete',
}).click();

cy.findByRole('alert', {}).should(
'have.text',
'The annotation has been deleted.',
);

cy.wait(500);

cy.findAllByRole('cell').then((cells) => {
const firstUri = cells[0].textContent;

expect(firstUri).to.match(/uid:\//);

expect(
cells.toArray().map((cell) => cell.textContent),
).to.deep.equal([
firstUri,
'RoboCop',
'rating',
'[bZE+]',
'',
'',
'',
'To Review',
'',
'',
'Jane Smith',
'This is another comment',
new Date().toLocaleDateString(),
new Date().toLocaleDateString(),
]);
});
});
});
});

Expand Down
7 changes: 4 additions & 3 deletions cypress/support/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ function createAnnotation({ buttonLabel, comment, authorName, authorEmail }) {

cy.wait(350);

cy.findByRole('textbox', { name: 'Name *', timeout: 1500 }).type(
authorName,
);
cy.findByRole('textbox', {
name: 'Last name, First name *',
timeout: 1500,
}).type(authorName);

if (authorEmail) {
cy.findByRole('textbox', { name: /Email/, timeout: 1500 }).type(
Expand Down
52 changes: 38 additions & 14 deletions src/api/controller/api/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,18 +246,8 @@ export async function getAnnotations(ctx) {
})),
};
}
/**
* @param {Koa.Context} ctx
*/
export async function getAnnotation(ctx, id) {
const annotation = await ctx.annotation.findOneById(id);

if (!annotation) {
ctx.response.status = 404;

return;
}

async function bindResourceAndFieldToAnnnotation(ctx, annotation) {
const [titleField, subResourceTitleFields] = await Promise.all([
ctx.field.findResourceTitle(),
ctx.field.findSubResourceTitles(),
Expand All @@ -271,8 +261,7 @@ export async function getAnnotation(ctx, id) {
? await ctx.field.findOneById(annotation.fieldId)
: null;

ctx.response.status = 200;
ctx.response.body = {
return {
...annotation,
field,
resource: resource
Expand All @@ -288,6 +277,25 @@ export async function getAnnotation(ctx, id) {
};
}

/**
* @param {Koa.Context} ctx
*/
export async function getAnnotation(ctx, id) {
const annotation = await ctx.annotation.findOneById(id);

if (!annotation) {
ctx.response.status = 404;

return;
}

ctx.response.status = 200;
ctx.response.body = await bindResourceAndFieldToAnnnotation(
ctx,
annotation,
);
}

/**
* @param {Koa.Context} ctx
*/
Expand All @@ -312,7 +320,22 @@ export async function updateAnnotation(ctx, id) {
}

ctx.response.status = 200;
ctx.response.body = { data: updatedAnnotation };
ctx.response.body = {
data: await bindResourceAndFieldToAnnnotation(ctx, updatedAnnotation),
};
}

export async function deleteAnnotation(ctx, id) {
const deletedCount = await ctx.annotation.deleteOneById(id);
if (deletedCount === 0) {
ctx.response.status = 404;
return;
}

ctx.response.status = 200;
ctx.response.body = {
success: true,
};
}

function getResourceTitle(resource, titleField, subResourceTitleFields) {
Expand All @@ -332,6 +355,7 @@ app.use(route.get('/', getAnnotations));
app.use(route.get('/:id', getAnnotation));
app.use(koaBodyParser());
app.use(route.put('/:id', updateAnnotation));
app.use(route.del('/:id', deleteAnnotation));
app.use(route.post('/', createAnnotation));

export default app;
82 changes: 81 additions & 1 deletion src/api/controller/api/annotation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import createFieldModel from '../../models/field';
import createPublishedDatasetModel from '../../models/publishedDataset';
import {
createAnnotation,
deleteAnnotation,
getAnnotation,
getAnnotations,
updateAnnotation,
Expand Down Expand Up @@ -1336,11 +1337,27 @@ describe('annotation', () => {
internalComment: 'All done',
administrator: 'The Tester',
updatedAt: expect.any(Date),
field: {
_id: field._id,
label: 'Annotated field',
name: 'GvaF',
position: 2,
},
resource: {
title: 'resource title',
uri: 'uid:/1234',
},
},
},
});
expect(await annotationModel.findLimitFromSkip()).toStrictEqual([
ctx.response.body.data,
{
...annotation,
status: 'validated',
internalComment: 'All done',
administrator: 'The Tester',
updatedAt: expect.any(Date),
},
]);
});

Expand Down Expand Up @@ -1407,4 +1424,67 @@ describe('annotation', () => {
]);
});
});

describe('DELETE /annotations/:id', () => {
let annotation;
beforeEach(async () => {
annotation = await annotationModel.create({
resourceUri: null,
itemPath: [],
fieldId: null,
authorName: 'Developer',
authorEmail: '[email protected]',
comment: 'This is a comment',
status: 'ongoing',
internalComment: null,
createdAt: new Date('03-01-2025'),
updatedAt: new Date('03-01-2025'),
});
});

it('should succeed with a 200 if annotation does not exist', async () => {
const ctx = {
request: {
body: {},
},
response: {},
annotation: annotationModel,
publishedDataset: publishedDatasetModel,
field: fieldModel,
};

await deleteAnnotation(ctx, annotation._id);

expect(ctx.response).toStrictEqual({
status: 200,
body: {
success: true,
},
});

expect(await annotationModel.findLimitFromSkip()).toStrictEqual([]);
});

it('should fail with a 404 if annotation does not exist', async () => {
const ctx = {
request: {
body: {},
},
response: {},
annotation: annotationModel,
publishedDataset: publishedDatasetModel,
field: fieldModel,
};

await deleteAnnotation(ctx, '404404404404404404404404');

expect(ctx.response).toStrictEqual({
status: 404,
});

expect(await annotationModel.findLimitFromSkip()).toStrictEqual([
annotation,
]);
});
});
});
19 changes: 18 additions & 1 deletion src/api/models/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,22 @@ export default async (db) => {
return annotationCollection.findOne({ _id: new ObjectId(id) });
}

return { create, updateOneById, findLimitFromSkip, count, findOneById };
async function deleteOneById(id) {
if (!ObjectId.isValid(id)) {
return 0;
}
const { deletedCount } = await annotationCollection.deleteOne({
_id: new ObjectId(id),
});
return +deletedCount;
}

return {
create,
updateOneById,
findLimitFromSkip,
count,
findOneById,
deleteOneById,
};
};
Loading

0 comments on commit 4bc97b1

Please sign in to comment.