notes icon indicating copy to clipboard operation
notes copied to clipboard

External API for attachments

Open korelstar opened this issue 2 years ago • 17 comments

#785 introduced a new functionality for attachments (see also #74). The internal API contains two new straight-forward endpoints:

  • POST /notes/{noteid}/attachment: uploads a new attachment to a note and returns the relative filepath
  • GET /notes/{noteid}/attachment?path={path}: downloads an existing attachment using the relative filepath

I would like to expose this functionality also to the external API so that third-party apps can use it. However, I'm not sure, if these endpoints are sufficient for you app developer @nextcloud/notes @stefan-niedermann. Therefore, please provide feedback!

What I have in mind is the following: when there are these two API changes only, your app will have to parse a note for image tags (e.g. ![label](relative image path)) and (optionally) links (e.g. [label](relative link path)) and then run subsequent requests to the GET attachment endpoint.

Another alternative is to do this parsing (also) on the server and provide a list of attachments together with the note's response (new attribute besides title, content etc). However, this will slow down synchronization and your app will still have to parse the note in order to show those images.

Hence, I vote for staying with these two changes (just add GET attachment and POST attachment) and the client will have to do the rest. Do you agree with this approach or do you see any other requirements for this API? @nextcloud/notes @stefan-niedermann

korelstar avatar Feb 27 '22 19:02 korelstar

I'd be happy to see this implemented! :tada:

muety avatar Mar 15 '22 07:03 muety

@korelstar sorry for the quite late reply - I did watch all changes of the Notes server repository and I am quite up to date. Please grant me some more time to review the concept of the attachment API, let's say one more week. I am unfortunately low on time (as always sigh 😮‍💨), but I am looking forward to implement the API in the Android app.

stefan-niedermann avatar Mar 20 '22 16:03 stefan-niedermann

@stefan-niedermann No problem, looking forward for your feedback :smiley:

korelstar avatar Mar 20 '22 18:03 korelstar

Alright, I had some time to read through the related issues and PRs. I know i am quite late to the party, but if possible, I'd like to discuss a few things (at least ask you to explain them 😉):

What I've understand so far

  1. In the markdown, the attachment paths are relative to the notes folder (the one which can also be changed via the settings API)? Example:
![Frog](../frog.jpg)

Given the notes folder is set to /Notes this means that the frog image is in the root directory, next to the /Notes folder, correct?

Clients are supposed to prefix the attachments api endpoint at rendering time in order to retrieve the actual file.

Open questions with this approach

  1. How do we deal with changing the notes folder? Does the notes server app update all notes? Will all attachments be broken?
  2. How do we deal with moving the attached files to other places?
  3. One key feature that I miss compared to the preview endpoint of Nextcloud files is to define a maximum size for x and y, so the response will get downscaled and cached as thumbnail before delivering it to the client. Would definitely appreciate if this could be adapted (given we stick with the attachments endpoints

Proposal for changes

Again, I am late to the party, so it's totally relatable to not make such deep changes anymore. Though I'd like to suggest and discuss some changes:

My first thought was to just use the fileId which does not change when moving stuff around. Querying content would also allow to access the preview endpoint.

Uploading a file

Just use the existing Nextcloud files API to upload the file. The fileId could be the link (maybe a second request needs to be done to retrieve the fileId):

![Frog](/f/123456)

Opening this as a plain link does work and will redirect you to the file in the Nextcloud files app (especially on Android, where users can be redirected to the Nextcloud Android app instead of just getting downloaded the attachment in a browser). This should also work nicely with other 3rd party apps like previewgenerator. Another advantage of this approach is that you wouldn't need to reinvent the wheel and add / maintain additional API endpoints rather than just use existing ones. The implementation could be completely possible on the client side (web interface in your case).

Downloading a file / Preview for rendering

You would need to rewrite the URL in the preview mode from

/f/123456 to /index.php/core/preview?fileId=123456 (optionally with x / y query params)

stefan-niedermann avatar Mar 22 '22 21:03 stefan-niedermann

Thanks for you feedback, @stefan-niedermann !

What I've understand so far

1. In the markdown, the attachment paths are relative to the notes folder (the one which can also be changed via the [settings API](https://github.com/nextcloud/notes/blob/master/docs/api/v1.md#settings))?

No, the attachment paths are relative to the note. E.g., if the notes path is /notes and the note's category is Animals, then the attachment path frog.jpg will be resolved to /notes/Animals/frog.jpg and the attachment path ../frog.jpg will be resolved to /notes/frog.jpg.

For security reasons, we've restricted the path resolution to the notes path, for now. E.g., if the notes path is /notes and the note's category is Animals, then the attachment path ../../frog.jpg is invalid.

Clients are supposed to prefix the attachments api endpoint at rendering time in order to retrieve the actual file.

No, clients do not need to worry about this. Clients just pass the notes ID and the relative attachment path to the API.

Open questions with this approach

  1. How do we deal with changing the notes folder? Does the notes server app update all notes? Will all attachments be broken?

No, since the path is relative to the notes path, it is fully transparent. But: Changing the note's category may lead to losing the connection to the attachment. This could be improved (see at the end of this comment).

  1. How do we deal with moving the attached files to other places?

Then the attachment can't be found anymore. But I think this is acceptable.

  1. One key feature that I miss compared to the preview endpoint of Nextcloud files is to define a maximum size for x and y, so the response will get downscaled and cached as thumbnail before delivering it to the client. Would definitely appreciate if this could be adapted (given we stick with the attachments endpoints

This works only for image attachments - which will be of course the most used case, but please note this circumstance.

Which size would be preferred? Should the client be able to define the size on its own?

Proposal for changes

Again, I am late to the party, so it's totally relatable to not make such deep changes anymore. Though I'd like to suggest and discuss some changes:

My first thought was to just use the fileId which does not change when moving stuff around. Querying content would also allow to access the preview endpoint.

I see two big disadvantages with the ID approach:

  • The file ID will change if you move your notes to another server. As a consequence, no attachment will work anymore.
  • The file ID is not very intuitive for humans. People, who work with the plain Markdown code, can't see which attachment is behind this ID.

Uploading a file

Just use the existing Nextcloud files API to upload the file.

As far as I can see, this API uses the WebDAV protocol. Currently, the notes API is kept very simple so that implementing clients is very easy. This would not be the case anymore. Furthermore, the client needs to derive the absolute path of the attachment - with all side effects.

The fileId could be the link (maybe a second request needs to be done to retrieve the fileId):

![Frog](/f/123456)

Opening this as a plain link does work and will redirect you to the file in the Nextcloud files app (especially on Android, where users can be redirected to the Nextcloud Android app instead of just getting downloaded the attachment in a browser). This should also work nicely with other 3rd party apps like previewgenerator. Another advantage of this approach is that you wouldn't need to reinvent the wheel and add / maintain additional API endpoints rather than just use existing ones. The implementation could be completely possible on the client side (web interface in your case).

I see your point, but I'm still not happy with the disadvantages of using the file ID (see above).

Looking at the Text app

The Nextcloud Text app inserts the following Markdown, when adding an image: ![image.jpg](image.jpg?fileId=556092#mimetype=image%2Fjpeg&hasPreview=true)

The main part is the human readable file path, followed by the file ID (and some other metadata - do we need them, too?).

What about transferring this approach to Notes? The API could take both file path and file ID. If there is no file with given path, then it would search for the file ID. However, the notes app should update the path, if a file is moved.

The big advantage: Markdown files created with the Notes app are compatible to those created with the Text app (and the other way around). I really think, we should keep this compatibility (especially if we are able to do #652 at some time in the future).

What do you think of this "compromise"?

korelstar avatar Mar 26 '22 13:03 korelstar

@korelstar As you already said, we should under no circumstance use the file-id as the main reference for the image. That would completely break any markdownviewer out there, and there is no way to 'manually' edit the files. However, adding get-parameters to the image.jpg should be possible, but we need to check if it is compliant and works with clients. (I checked my desktop-client, and adding a query does not work.) We should be very careful with that.

In my opinion, we should not touch the filepath at all. If we need parameter, we should include them in the api endpoint, not in the filepath that is stored in the markdown.

GET /notes/{noteid}/attachment/{size}?path= or whatever we need.

The question is: do we even need anything besides the noteid and the path? How would the file ID or metadata be relevant, since we only have to render the image as per markdown?

Also we should be very careful what we import from the text-app, since they basically break most conventions that markdown should stick to. The Text-App should not be our role model.

newhinton avatar Mar 30 '22 10:03 newhinton

Alright, thanks for the clarification!

Which size would be preferred? Should the client be able to define the size on its own?

Yes, ideally just like the default files API by defining x and y query params, semantically meaning that the image should be downscaled to a size that fits into the dedined size ("contains"). Ignore the params if the file is not an image / can not be downscaled.

In my opinion, we should not touch the filepath at all. If we need parameter, we should include them in the api endpoint, not in the filepath that is stored in the markdown.

Just as additional query params like

GET /notes/{noteid}/attachment?path=frog.jpg&x=1920&y=1080

(Quite selfish, but the nextcloud-commons glide module can already handle this automatically by the screen size 😜)

I agree with you that the approach is good and we don't need to change anything to file IDs or so 👍.

stefan-niedermann avatar Mar 30 '22 15:03 stefan-niedermann

GET /notes/{noteid}/attachment?path=frog.jpg&x=1920&y=1080

It seems i had a moment of clouded judgement, this is no problem at all since the params could be handled by the client outside of markdown. I have to retract my objections, this would be just fine!

Does the glide module append the x&y params? Or how does it handle it? I have only implemented url-rewriting, so i didn't stumble across the actual handling of the image request. Though it did seem to work out of the box after a proper url was provided after parsing the markdown, including caching

newhinton avatar Mar 31 '22 18:03 newhinton

Is there anything that needs to be done to publish this? It seems to me that there are no open questions and all requirements are satisfied, so we should be able to finalize this and get that functionality into the apps

newhinton avatar Jul 11 '22 10:07 newhinton

Yes, there is not much to be done. However, I didn't find the time for this. If you want to help, I would be very happy. Open tasks are:

  • add API endpoint to NotesApiController
    • It should be evaluated, if the download method could just redirect to the existing preview service of the Nextcloud files API. This would be better than implementing the resize feature in the notes app.
  • add some tests to APIv1Test
    • These have to be done wise - there should be also tests on problematic cases and security issues.
  • add documentation to API docs

korelstar avatar Aug 05 '22 20:08 korelstar

It should be evaluated, if the download method could just redirect to the existing preview service of the Nextcloud files API

@korelstar did you already have some time to evaluate whether it is possible to forward x and y query arguments to the preview API? For a mobile client it is essential in my opinion to not transfer the full sized images. I'd love to implement support i the Android client, so I'm looking forward to the official API 😉

stefan-niedermann avatar Sep 05 '22 07:09 stefan-niedermann

@stefan-niedermann Sorry for being late on this. But yes, my plan is to re-use the existing preview API and provide sizing parameters in the Notes API. Hopefully, I will soon find some time to implement this.

korelstar avatar Oct 01 '22 15:10 korelstar

The richtext editor already support this. Is it wanted to support the edit/preview mode parallel to the richtext editor ?

theatischbein avatar Mar 25 '23 09:03 theatischbein

This issue is about the external API which is used by e.g. the Android app. Therefore, also the rich text editor does not support this yet. Please see here for the steps to be done: https://github.com/nextcloud/notes/issues/826#issuecomment-1206832073 This would be the same for both types of editors.

korelstar avatar Mar 25 '23 10:03 korelstar

Thanks for your answer! I am still a bit confused. The richtext editor uses the following endpoint: POST http://localhost:8080/apps/text/attachment/upload?documentId=570&sessionId=176&sessionToken=token&shareToken= for uploading attachements. This should also be usable/accessable for the Android app, when using the richtext editor , shouldn't it ?

I would like to give it try to implement the API endpoint, but still not get the idea to expose this feature to third party apps.

And I do get your answer right, that both editors should be supported ?

theatischbein avatar Mar 26 '23 07:03 theatischbein