Skip to content

API: Fix tags update on updating a note #2649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions CliClient/app/command-apidoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,11 @@ class Command extends BaseCommand {
type: Database.enumId('fieldType', 'text'),
description: 'If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`',
});
// tableFields.push({
// name: 'tags',
// type: Database.enumId('fieldType', 'text'),
// description: 'Comma-separated list of tags. eg. `tag1,tag2`.',
// });
tableFields.push({
name: 'tags',
type: Database.enumId('fieldType', 'text'),
description: 'Comma-separated list of tags. eg. `tag1,tag2`.',
});
}

lines.push(`# ${toTitleCase(tableName)}`);
Expand Down
81 changes: 81 additions & 0 deletions CliClient/tests/services_rest_Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const Folder = require('lib/models/Folder');
const Resource = require('lib/models/Resource');
const Note = require('lib/models/Note');
const Tag = require('lib/models/Tag');
const NoteTag = require('lib/models/NoteTag');
const { shim } = require('lib/shim');

jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
Expand Down Expand Up @@ -326,4 +327,84 @@ describe('services_rest_Api', function() {
expect(response3.length).toBe(2);
}));

it('should update tags when updating notes', asyncTest(async () => {
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
const tag3 = await Tag.save({ title: 'mon étiquette 3' });

const note = await Note.save({
title: 'ma note un',
});
Tag.addNote(tag1.id, note.id);
Tag.addNote(tag2.id, note.id);

const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
tags: `${tag1.title},${tag3.title}`,
}));
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
expect(response.tags === `${tag1.title},${tag3.title}`).toBe(true);
expect(tagIds.length === 2).toBe(true);
expect(tagIds.includes(tag1.id)).toBe(true);
expect(tagIds.includes(tag3.id)).toBe(true);
}));

it('should create and update tags when updating notes', asyncTest(async () => {
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
const newTagTitle = 'mon étiquette 3';

const note = await Note.save({
title: 'ma note un',
});
Tag.addNote(tag1.id, note.id);
Tag.addNote(tag2.id, note.id);

const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
tags: `${tag1.title},${newTagTitle}`,
}));
const newTag = await Tag.loadByTitle(newTagTitle);
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
expect(response.tags === `${tag1.title},${newTag.title}`).toBe(true);
expect(tagIds.length === 2).toBe(true);
expect(tagIds.includes(tag1.id)).toBe(true);
expect(tagIds.includes(newTag.id)).toBe(true);
}));

it('should not update tags if tags is not mentioned when updating', asyncTest(async () => {
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
const tag2 = await Tag.save({ title: 'mon étiquette 2' });

const note = await Note.save({
title: 'ma note un',
});
Tag.addNote(tag1.id, note.id);
Tag.addNote(tag2.id, note.id);

const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
title: 'Some other title',
}));
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
expect(response.tags === undefined).toBe(true);
expect(tagIds.length === 2).toBe(true);
expect(tagIds.includes(tag1.id)).toBe(true);
expect(tagIds.includes(tag2.id)).toBe(true);
}));

it('should remove tags from note if tags is set to empty string when updating', asyncTest(async () => {
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
const tag2 = await Tag.save({ title: 'mon étiquette 2' });

const note = await Note.save({
title: 'ma note un',
});
Tag.addNote(tag1.id, note.id);
Tag.addNote(tag2.id, note.id);

const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
tags: '',
}));
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
expect(response.tags === '').toBe(true);
expect(tagIds.length === 0).toBe(true);
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, for future maintenance, it would be better to separate the test cases into 3 or 4 smaller test cases rather than one big one. This also helps avoid using the 1,2,3,4 variable names too. Sometimes it is too inefficient, but by default it is preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I restructured all the test cases into separate ones. But, I'm not sure if I did it right. I have used lengthy test case descriptions for some test cases. Please review and let me know if I'm doing it right.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

});
Copy link
Contributor

@miciasto miciasto Mar 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the user accidently sets a tag name that doesn't exist? Is it possible to do via this interface? If it is, then we should add a test for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user sets a tag name that doesn't exist via this interface, it creates the tags and then adds it to the note. I forgot to write a test case for that. I will fix the unit test.

16 changes: 16 additions & 0 deletions ReactNativeClient/lib/services/rest/Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,22 @@ class Api {
return note;
}

if (request.method === 'PUT') {
const note = await Note.load(id);

if (!note) throw new ErrorNotFound();

let updatedNote = await this.defaultAction_(BaseModel.TYPE_NOTE, request, id, link);

const requestNote = JSON.parse(request.body);
if (requestNote.tags || requestNote.tags === '') {
const tagTitles = requestNote.tags.split(',');
await Tag.setNoteTagsByTitles(id, tagTitles);
}

return updatedNote;
}

return this.defaultAction_(BaseModel.TYPE_NOTE, request, id, link);
}

Expand Down