-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
Conversation
@PackElend label me please |
|
||
if (!note) throw new ErrorNotFound(); | ||
|
||
let newNote = Object.assign({}, note, request.bodyJson(this.readonlyProperties('PUT'))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with this interface so please help me understand. Is the PUT
command intended to update an existing note? Is this really a new note, or an updated one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the PUT
command is intended to update an existing note. Perhaps, the variable name newNote
was a bit confusing? I will change it to updatedNote
.
if (!note) throw new ErrorNotFound(); | ||
|
||
let newNote = Object.assign({}, note, request.bodyJson(this.readonlyProperties('PUT'))); | ||
newNote = await Note.save(newNote, { userSideValidation: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the effect of user-side validation here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code I have written here was actually refactored from the defaultAction_
function which was previously called in the case of PUT
methods.
To be honest, I do not exactly know what user-side validation does here. But since it was already present in the code that was executed (before this fix), I decided to leave it untampered in order not to break any functionality.
CliClient/tests/services_rest_Api.js
Outdated
const tag3 = await Tag.save({ title: 'mon étiquette 3' }); | ||
const note = await Note.save({ | ||
title: 'ma note un', | ||
tags: `${tag1.title},${tag2.title}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this add the tags correclty? Perhaps in the test you can start checking if the note is set up as you intended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it does add the tags in the note (checked in the desktop app). But I haven't checked the note in the unit test. I will rewrite the unit test to make sure it tests the notes rather than the response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you have the response checks, they are still useful. So just add the new checks, so it checks both.
tags: `${tag1.title},${tag2.title},${tag3.title}`, | ||
})); | ||
expect(response3.tags === `${tag1.title},${tag2.title},${tag3.title}`).toBe(true); | ||
})); | ||
}); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this testing, you are checking the API response to confirm if the tags are set. In the issue, it looks like the responses were OK, but the actual database was not updated.
Should you also check the actual tags on the note note after each PUT
(using Tag
and/or Note
methods to directly interrogate the associated tags)? I'm not sure the test is covering the issue.
Yes, you were right. I should have checked the actual tags on the notes themselves. I have since rewritten the unit tests to do this. |
I have done all the requested changes. Please review :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jyuvaraj03, just a few more comments, all minor except one.
CliClient/tests/services_rest_Api.js
Outdated
expect(tagIds1[0]).toBe(tag1.id); | ||
expect(tagIds1[1]).toBe(tag3.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although this might work, there is no guarantee the tags will be returned in this order. Please modify so it is not dependent on the order.
@@ -451,6 +451,23 @@ class Api { | |||
return note; | |||
} | |||
|
|||
if (request.method === 'PUT') { | |||
const note = await Note.load(id); | |||
const requestBody = JSON.parse(request.body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency we should change the name of 'requestBody' to match what is used in the 'POST' code (just above).
Also there is no need to create this so early in the function, so please move it closer to where it is first used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also there is no need to create this so early in the function, so please move it closer to where it is first used.
I have made this change. But, can I know why it is important to not create it early in the function?
|
||
if (requestBody.tags) { | ||
const tagTitles = requestBody.tags.split(','); | ||
await Tag.setNoteTagsByTitles(updatedNote.id, tagTitles); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updatedNote.id
can be just id
let updatedNote = Object.assign({}, note, request.bodyJson(this.readonlyProperties('PUT'))); | ||
updatedNote = await Note.save(updatedNote, { userSideValidation: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mentioned you borrowed this from the default handler. So rather than duplicate the code here, why not call the default handler to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't think about that! I'll implement it now.
Completed all the requested changes. Please review! |
@mic704b, I've sent you an invite to be a collaborator on the project, so when you think this one is ready feel free to merge it. Please use the syntax |
Sorry, I accidentally requested a review! Please review at your own pace. |
Thank you @laurent22! I am honoured to be invited. |
@jyuvaraj03 looks good, nearly there. |
Thanks for pointing it out. That was an excellent catch! I hadn't handled that case. When the tags in the request is empty, the tags associated with the note do not get deleted like intended. Instead, they stay the same. So I handled it by checking if the |
CliClient/tests/services_rest_Api.js
Outdated
const response4 = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({ | ||
tags: '', | ||
})); | ||
const tagIds4 = await NoteTag.tagIdsByNoteId(note.id); | ||
expect(response4.tags === '').toBe(true); | ||
expect(tagIds4.length === 0).toBe(true); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this test case, the tags
property is mentioned explicitly with a ''
. So, all the tags associated with the note are removed from the note.
CliClient/tests/services_rest_Api.js
Outdated
const response3 = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({ | ||
title: 'Some other title', | ||
})); | ||
const tagIds3 = await NoteTag.tagIdsByNoteId(note.id); | ||
expect(tagIds3.length === 2).toBe(true); | ||
expect(tagIds3.includes(tag2.id)).toBe(true); | ||
expect(tagIds3.includes(newTag.id)).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test case checks the case when the tags
property is not mentioned during the request. So, the tags associated with the note does not differ from the previous test case.
Also, I haven't checked response3.tags
because it is undefined
when tags
property isn't mentioned during the API request. So, I skipped that check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because it is
undefined
whentags
property isn't mentioned
That can be tested too! If it wasn't returned as undefined, then it would be a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, a few more minor comments.
One more question: does the API documentation need update? The documentation says PUT on a note is to set note properties, but doesn't list tags as a property. Perhaps it should be added to the list, or the description changed to something like Sets the properties and tags of ...
. What do you think?
If it needs change, please do include it on this PR.
if (requestNote.tags === '') { | ||
await Tag.setNoteTagsByTitles(id, ''); | ||
} else if (requestNote.tags) { | ||
const tagTitles = requestNote.tags.split(','); | ||
await Tag.setNoteTagsByTitles(id, tagTitles); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be reduced to a single if statement? I don't think you need two branches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I just realized two branches weren't necessary. I changed the code to use just a single if
.
const tagIds4 = await NoteTag.tagIdsByNoteId(note.id); | ||
expect(response4.tags === '').toBe(true); | ||
expect(tagIds4.length === 0).toBe(true); | ||
})); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
CliClient/tests/services_rest_Api.js
Outdated
const response3 = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({ | ||
title: 'Some other title', | ||
})); | ||
const tagIds3 = await NoteTag.tagIdsByNoteId(note.id); | ||
expect(tagIds3.length === 2).toBe(true); | ||
expect(tagIds3.includes(tag2.id)).toBe(true); | ||
expect(tagIds3.includes(newTag.id)).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because it is
undefined
whentags
property isn't mentioned
That can be tested too! If it wasn't returned as undefined, then it would be a problem.
That's a good point, the doc should be updated. To do so @jyuvaraj03 you can update command-apidoc.js, then you can verify the output from /CliClient using In fact, ideally we should support all the special properties that the POST method supports, such as body_html, image_data_url, crop_rect, etc. for consistency. That could be done with a bit of refactoring, but it's not needed for this PR. |
You're right. So, I updated the doc. I added the |
Yes, I did update the command-apidoc.js file. But, I have a small question. These were the lines of code I changed in that file:
I just uncommented them in this commit. These lines already existed, but you had commented them out. Was there a reason you did that? Am I missing something?
I would like to work on implementing these (possibly in another PR). How should I go about it? Should I create an issue for this and then submit a PR? Also, @PackElend can you please @mention Laurent here because he was the one who commented those lines out, and I would like to know why he did that. |
Ok I think it's ready to merge now, thanks @jyuvaraj03 and thanks @mic704b for reviewing! |
Definitely. Good work @jyuvaraj03 |
Fixes #941
Before:

After:

The issue as mentioned in #941 was that the tags were not updated. This was due to the fact that in
PUT
request method the tags were not explicitly set unlike how thePOST
request method was handled in theaction_notes
function.The fix I have given makes the necessary changes. I have also written unit tests for the code. Looking forward to any comments or suggestions!