Skip to content

Commit

Permalink
deprecate file upload and implement new apis
Browse files Browse the repository at this point in the history
  • Loading branch information
koonwen committed Jan 3, 2025
1 parent 3805dc0 commit 7ea6450
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 1 deletion.
8 changes: 8 additions & 0 deletions lib/api.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ module type S = sig
val send_message_webhook : ctx:Context.t -> url:string -> msg:post_message_req -> unit slack_response Lwt.t
val update_message : ctx:Context.t -> msg:update_message_req -> update_message_res slack_response Lwt.t
val upload_file : ctx:Context.t -> file:files_upload_req -> files_upload_res slack_response Lwt.t
[@@deprecated "Use get_upload_url_external with \
complete_upload_external as per \
https://api.slack.com/messaging/files#uploading_files. Otherwise, \
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
val upload_file_v2 : ctx:Context.t -> file:files_upload_req -> files_upload_res slack_response Lwt.t

(* conversations *)
val get_replies :
Expand Down
33 changes: 33 additions & 0 deletions lib/api_local.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ let default_files_res : Slack_t.file =

let default_files_upload_res : Slack_t.files_upload_res = { ok = true; file = default_files_res }

let default_files_upload_res_v2 : Slack_t.complete_upload_ext_res =
{ ok = true; files = [ { id = default_files_res.id; title = default_files_res.title } ] }

let send_message ~ctx:_ ~msg =
let json = msg |> Slack_j.string_of_post_message_req |> Yojson.Basic.from_string |> Yojson.Basic.pretty_to_string in
printf "will notify #%s\n" msg.channel;
Expand Down Expand Up @@ -65,6 +68,36 @@ let upload_file ~ctx:_ ~file =
Lwt.return_ok { default_files_upload_res with file = { default_files_res with channels = [ channels ] } }
| None -> Lwt.return_error (`Other "invalid file upload")

let get_upload_url_external ~ctx:_ ~(req : Slack_t.get_upload_url_ext_req) =
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;
Lwt.return_ok Slack_t.{ ok = true; upload_url = "http://fake-upload-url.com"; file_id = "file_id" }

let complete_upload_external ~ctx:_ ~(req : Slack_t.complete_upload_ext_req) =
let json =
req |> Slack_j.string_of_complete_upload_ext_req |> Yojson.Basic.from_string |> Yojson.Basic.pretty_to_string
in
printf "complete upload for %s in channel:%s or ts:%s\n" (Slack_j.string_of_files_v2 req.files)
(Option.default "NULL" req.channel_id) (Option.default "NULL" req.thread_ts);
printf "%s\n" json;
Lwt.return_ok default_files_upload_res_v2

let upload_file_v2 ~ctx:_ ~(file : Slack_t.files_upload_req) =
let json = file |> Slack_j.string_of_files_upload_req |> Yojson.Basic.from_string |> Yojson.Basic.pretty_to_string in
match file.channels with
| Some channels ->
if List.length (String.split_on_char ',' channels) <> 1 then
Lwt.return_error (`Other "only allowed to specify 1 channel")
else (
printf "will update #%s\n" channels;
printf "%s\n" json;
Lwt.return_ok { default_files_upload_res with file = { default_files_res with channels = [ channels ] }}
)
| None -> Lwt.return_error (`Other "invalid file upload")

let join_conversation ~ctx:_ ~(channel : Slack_t.conversations_join_req) =
printf "joining #%s...\n" channel.channel;
let url = Filename.concat cache_dir (sprintf "%s_join" channel.channel) in
Expand Down
64 changes: 64 additions & 0 deletions lib/api_remote.ml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,70 @@ let upload_file ~(ctx : Context.t) ~(file : Slack_t.files_upload_req) =
~name:(sprintf "file.upload (%s)" @@ Option.default "<NO CHANNEL>" file.channels)
~body `POST "files.upload" Slack_j.read_files_upload_res

let www_form_of_get_upload_url_ext (req : Slack_t.get_upload_url_ext_req) =
let fields =
[
Some ("filename", req.filename);
Some ("length", Int.to_string req.length);
string_field_val req.alt_txt "alt_txt";
string_field_val req.snippet_type "snippet_type";
]
in
list_filter_opt fields

let get_upload_url_external ~(ctx : Context.t) ~(req : Slack_t.get_upload_url_ext_req) =
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
log#info "data to upload req: %s" data;
request_token_auth ~ctx
~name:(sprintf "files.getUploadURLExternal (%s)" req.filename)
~body `POST "files.getUploadURLExternal" Slack_j.read_get_upload_url_ext_res

let complete_upload_external ~(ctx : Context.t) ~(req : Slack_t.complete_upload_ext_req) =
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;
request_token_auth ~ctx
~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 *)
let upload_file_v2 ~(ctx : Context.t) ~(file : Slack_t.files_upload_req) =
match file.filename, file.content with
| None, _ | _, None -> Exn.fail "need to supply both filename and content"
| Some filename, Some content ->
let length = String.length 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; _ } ->
let body = `Form [ "file", filename ] in
( match%lwt http_request ~ua:ctx.ua ~body `POST upload_url with
| Error e -> slack_lib_fail "upload file failed with: %s" e
| Ok _ ->
let files : Slack_t.file_v2 = { id = file_id; title = file.title } in
let req =
Slack_j.make_complete_upload_ext_req ~files:[ files ] ?channel_id:file.channels ?thread_ts:file.thread_ts ()
in
( match%lwt complete_upload_external ~ctx ~req with
| Error e -> Lwt.return_error e
| Ok { files; _ } ->
let f =
match files with
| [] -> slack_lib_fail "empty files on complete_upload_external response"
| [ f ] -> f
| f :: _ ->
log#warn "got more than 1 file in response, returning top";
f
in
Lwt.return_ok Slack_t.{ ok = true; file = Slack_j.make_file ~id:f.id ?title:f.title () }
)
)
)

(** [join_conversation ctx channel ] will join the token owner
[ctx.secrets.slack_access_token] to the [channel]. *)
let join_conversation ~(ctx : Context.t) ~(channel : Slack_t.conversations_join_req) =
Expand Down
34 changes: 33 additions & 1 deletion lib/slack.atd
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ type files_upload_req <ocaml attr="deriving make"> = {
?title: string nullable;
}

type file = {
type file <ocaml attr="deriving make"> = {
id: string;
?name: string nullable;
?title: string nullable;
Expand All @@ -151,6 +151,38 @@ type files_upload_res = {
file: file;
}

type file_v2 = {
id: string;
?title : string nullable
}

type files_v2 = file_v2 list

type get_upload_url_ext_req <ocaml attr="deriving make"> = {
filename: string;
length: int;
?alt_txt: string nullable;
?snippet_type: string nullable
}

type get_upload_url_ext_res = {
ok: bool;
upload_url: string;
file_id: string
}

type complete_upload_ext_req <ocaml attr="deriving make"> = {
files: files_v2;
?channel_id : string nullable;
?initial_comment: string nullable;
?thread_ts: string nullable
}

type complete_upload_ext_res = {
ok: bool;
files: files_v2;
}

type conversations_info_req <ocaml attr="deriving make"> = {
channel: string;
?include_locale: bool nullable;
Expand Down

0 comments on commit 7ea6450

Please sign in to comment.