Skip to content
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

deprecate file.upload and implement new apis #11

Merged
merged 7 commits into from
Feb 7, 2025
Merged

Conversation

koonwen
Copy link
Contributor

@koonwen koonwen commented Jan 3, 2025

Related to #5

I did some initial scaffolding work around this. Haven't done any testing yet so it probably doesn't work.

Some caveats about the new api is that it is a three step query process

  1. get a url to upload to
  2. actually upload the file to the url
  3. specify where the uploaded file should appear in slack

Also with the new api, we cannot specify multiple channels in 3. and you can only call it once which I guess means once you upload it once, it gets thrown away after. https://api.slack.com/methods/files.getUploadURLExternal#markdown I thought about multiplexing the request by getting multiple upload urls but I don't know if this will work as expected and slack will give back multiple unique ids to upload to. That's also kinda inefficient to duplicate the upload work.

In any case, feel free to take over this PR

@koonwen koonwen requested a review from sewenthy January 3, 2025 07:43
@koonwen koonwen mentioned this pull request Jan 3, 2025
@sewenthy
Copy link
Contributor

sewenthy commented Jan 6, 2025

I feel like this change with additional upload_file_v2 is a bit hard to manage, I would rather remove the deprecated version. Honestly, not sure how to handle deprecation in opensource libraries 😅 @Khady if you have any suggestions!

lib/api_remote.ml Show resolved Hide resolved
lib/api.ml Outdated

val get_upload_url_external : ctx:Context.t -> req:get_upload_url_ext_req -> get_upload_url_ext_res slack_response Lwt.t
val complete_upload_external : ctx:Context.t -> req:complete_upload_ext_req -> complete_upload_ext_res slack_response Lwt.t
val upload_file_v2 : ctx:Context.t -> file:files_upload_req -> files_upload_res slack_response Lwt.t

Choose a reason for hiding this comment

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

I know other slack SDK have used the v2 naming scheme, but since we have the exact same type, I don't see the point. What downside is there to just replacing upload_file ?

lib/api.ml Outdated
there is upload_file_v2 convenience function"]

val get_upload_url_external : ctx:Context.t -> req:get_upload_url_ext_req -> get_upload_url_ext_res slack_response Lwt.t
val complete_upload_external : ctx:Context.t -> req:complete_upload_ext_req -> complete_upload_ext_res slack_response Lwt.t

Choose a reason for hiding this comment

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

Why expose the substeps ? If we do not, it can also be remove from the api_local implementation.

Is it in the case that you want to do multiple uploads of the same file ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It gives us the flexibility to upload multiple files* simultaneously if that's desired in the future. But I see your point, to cut down on the extra cruft I'll hide it from the signature

~name:(sprintf "files.completeUploadExternal (%s)" @@ Slack_j.string_of_files_v2 req.files)
~body `POST "files.completeUploadExternal" Slack_j.read_complete_upload_ext_res

(** NOTE: this api only can specify one channel_id unlike in the deprecated version *)

Choose a reason for hiding this comment

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

It can actually specify multiple channels. The channel_id field only excpects 1, but there is a channels field where you can add multiples. https://api.slack.com/methods/files.completeUploadExternal

@koonwen koonwen force-pushed the new_upload_api branch 3 times, most recently from 3f022a4 to bf2d4ac Compare February 7, 2025 06:05
@koonwen koonwen merged commit 365868f into master Feb 7, 2025
8 checks passed
Copy link

@thatportugueseguy thatportugueseguy left a comment

Choose a reason for hiding this comment

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

please revisit this code and add support for snippets that don't come from a file and better error handling.

Add tests to the backlog, if it will take you too much time

Choose a reason for hiding this comment

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

these kinds of changes should be sent directly to master. These are unrelated to the PR

Comment on lines +76 to +80
let json =
req |> Slack_j.string_of_get_upload_url_ext_req |> Yojson.Basic.from_string |> Yojson.Basic.pretty_to_string
in
printf "getting upload url for %s\n" req.filename;
printf "%s\n" json;

Choose a reason for hiding this comment

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

isn't this debugging logic?

Are you adding tests to these functions? it seems that this logic should live in the test file?

(same below, for complete_upload_external)

let args = www_form_of_get_upload_url_ext req in
let data = Web.make_url_args args in
let body = `Raw ("application/x-www-form-urlencoded", data) in
log#info "data to upload req: %s" data;

Choose a reason for hiding this comment

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

should these be log#debug? I'm not sure that the users want to log this data for all requests

log#info "completing upload url for %s" @@ Slack_j.string_of_files_v2 req.files;
let data = Slack_j.string_of_complete_upload_ext_req req in
let body = `Raw ("application/json", data) in
log#info "data to upload req: %s" data;

Choose a reason for hiding this comment

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

same as above

Comment on lines +138 to +139
match file.filename, file.content with
| None, _ | _, None -> Exn.fail "need to supply both filename and content"

Choose a reason for hiding this comment

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

i think it would make more sense to separate the concerns regarding the file itself and the upload config. These are all "crammed" in files_upload_req, but could easily be separated.

Or don't call the argument file, which it's not.

| Error e -> Lwt.return_error e
| Ok { upload_url; file_id; _ } ->
let raw_file_contents = In_channel.with_open_bin filename (fun ic -> input_all ic) in
let body = `Raw ("", raw_file_contents) in

Choose a reason for hiding this comment

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

Suggested change
let body = `Raw ("", raw_file_contents) in
let body = `Raw ("text/plain", raw_content) in

let req = Slack_j.make_get_upload_url_ext_req ~filename ~length () in
( match%lwt get_upload_url_external ~ctx ~req with
| Error e -> Lwt.return_error e
| Ok { upload_url; file_id; _ } ->

Choose a reason for hiding this comment

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

slack api fails with a 200 OK response but sends the ok property of the payload as false and includes an error property on the response.

Error responses aren't properly handled here or below, after the complete_upload_external response.

let req = Slack_j.make_complete_upload_ext_req ~files ?channels:file.channels ?thread_ts:file.thread_ts () in
( match%lwt complete_upload_external ~ctx ~req with
| Error e -> Lwt.return_error e
| Ok { files; _ } ->

Choose a reason for hiding this comment

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

see comment above regarding slack failing with 200 OK

log#info "getting upload url for %s" req.filename;
let args = www_form_of_get_upload_url_ext req in
let data = Web.make_url_args args in
let body = `Raw ("application/x-www-form-urlencoded", data) in

Choose a reason for hiding this comment

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

if you want to use application/x-www-form-urlencoded here instead of text/plain, you should use `Form instead of `Raw

@@ -113,6 +100,72 @@ let get_permalink ~(ctx : Context.t) ~(req : Slack_t.get_permalink_req) =
~name:(sprintf "chat.getPermalink (%s, %s)" req.channel req.message_ts)
~ctx `GET api_path Slack_j.read_get_permalink_res

let www_form_of_get_upload_url_ext (req : Slack_t.get_upload_url_ext_req) =

Choose a reason for hiding this comment

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

is this function used elsewhere? i don't think it's worth having this function out of get_upload_url_external. Just declare the fields variable and inside of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants