Skip to content

Conversation

YutoKashiwagi
Copy link
Contributor

@YutoKashiwagi YutoKashiwagi commented Feb 19, 2025

Hi, thank you for maintaining slack-go!

Currently, UploadFileV2Context works by sequentially calling three private methods in the following flow:

  1. getUploadURLExternal (retrieves the upload URL)
  2. uploadToURL (uploads the file to that URL)
  3. completeUploadExternal (finalizes the upload)

Because these methods are private, developers can only upload one file per message via UploadFileV2Context. However, the Slack API itself supports uploading multiple files in a single message by running these steps for each file and then calling completeUploadURLExternal for all file IDs together.

To unlock this functionality while still using slack-go, I’ve made the following changes in this PR:

  • Made getUploadURLExternal and completeUploadExternal public
    • This allows developers to orchestrate multiple file uploads (Steps 1–3 repeated for each file) and finalize them all in a single message.
  • uploadToURL remains private for now. This method is simply a regular POST request handler, not part of Slack’s official API. Since making getUploadURLExternal and completeUploadExternal public already addresses the main use case of uploading multiple files, this might be sufficient.
    • However, if you feel uploadToURL should also be made public for consistency, please let me know and I can include that change in this PR.

Thank you for considering this proposal. Being able to handle multi-file uploads in a single Slack message through slack-go would be extremely helpful. If you have any suggestions or an alternative approach, I’d love to hear them!

Related Issues

Pull Request Guidelines

These are recommendations for pull requests.
They are strictly guidelines to help manage expectations.

PR preparation

Run make pr-prep from the root of the repository to run formatting, linting and tests.

Should this be an issue instead
  • is it a convenience method? (no new functionality, streamlines some use case)
  • exposes a previously private type, const, method, etc.
  • is it application specific (caching, retry logic, rate limiting, etc)
  • is it performance related.
API changes

Since API changes have to be maintained they undergo a more detailed review and are more likely to require changes.

  • no tests, if you're adding to the API include at least a single test of the happy case.
  • If you can accomplish your goal without changing the API, then do so.
  • dependency changes. updates are okay. adding/removing need justification.
Examples of API changes that do not meet guidelines:
  • in library cache for users. caches are use case specific.
  • Convenience methods for Sending Messages, update, post, ephemeral, etc. consider opening an issue instead.

Comment on lines +288 to +302
type mockGetUploadURLExternalHttpClient struct {
ResponseStatus int
ResponseBody []byte
}

func (m *mockGetUploadURLExternalHttpClient) Do(req *http.Request) (*http.Response, error) {
if req.URL.Path != "files.getUploadURLExternal" {
return nil, fmt.Errorf("invalid path: %s", req.URL.Path)
}

return &http.Response{
StatusCode: m.ResponseStatus,
Body: io.NopCloser(bytes.NewBuffer(m.ResponseBody)),
}, nil
}
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 considered following the existing once.Do(startServer) approach for testing, but found that simply mocking Client.httpclient would be sufficient. Therefore, I decided to mock it instead.

Comment on lines +627 to +630
Files: []FileSummary{{
ID: u.FileID,
Title: params.Title,
}},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For CompleteUploadURLExternal, I've not only made it public but also updated its signature so that multiple fileID parameters (with IDs and titles) can be specified, aligning it more closely with Slack’s official API.

@sheldonhull
Copy link

thanks for doing this. I just was revisiting a way to grab the urls so I could publish a single "gallery style" message and this probably will fix much of that by providing back the upload urls back. cheers 🙇

@nlopes nlopes self-requested a review February 27, 2025 19:53
@nlopes nlopes self-assigned this Feb 27, 2025
Copy link
Collaborator

@nlopes nlopes left a comment

Choose a reason for hiding this comment

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

@YutoKashiwagi thank you so much for sending this and providing a well reasoned approach here.

Also very smart to match with the ability to send multiple files per the Slack API as part of opening this up to users of the library.

There's a tiny change I think is worth doing and once that's out of the way I will ✅ this.

files.go Outdated
// Slack API docs: https://api.slack.com/methods/files.getUploadURLExternal
func (api *Client) GetUploadURLExternalContext(ctx context.Context, params GetUploadURLExternalParameters) (*GetUploadURLExternalResponse, error) {
if params.FileName == "" {
return nil, fmt.Errorf("GetUploadURLExternalContext: fileName cannot be empty")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you mind removing GetUploadURLExternalContext from the error? Happy for the message to indicate where it's at but this format doesn't quite fit with everything else.

I am aware we already have file.upload.v2: in fmt.Errorf in this file so happy if you want to change to be sort of that format but what I'd really prefer is to not have the name of the function in the error returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I’ve updated it to include the API name instead of the function name.

9a7fd29

@YutoKashiwagi YutoKashiwagi requested a review from nlopes March 2, 2025 14:38
@nlopes nlopes self-requested a review March 2, 2025 19:10
Copy link
Collaborator

@nlopes nlopes left a comment

Choose a reason for hiding this comment

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

I missed these so adding them here and will resolve in a sec.

@nlopes nlopes merged commit 80b52b0 into slack-go:master Mar 2, 2025
7 checks passed
@sheldonhull
Copy link

thank you for this! I'm not seeing a release containing it, so do you have a general timeframe @nlopes you plan on cutting a release? If I missed something on this already being said, my apologies 🙇🏻

@nlopes
Copy link
Collaborator

nlopes commented Mar 5, 2025

, so do you have a general timeframe @nlopes you plan on cutting a release

I intend to cut a breaking change release this coming weekend, stay tuned 🙏

@sheldonhull
Copy link

Quick follow-up. I'm trying to use this to handle multiple generated images being uploaded at once, but I think the part I'm missing for this to be useful is uploadToURL which is private.

Is there further changes I'm missing for uploadToURL being visible? I think as I'm working through my scenario to upload all at once into a single message it won't work without this.

CleanShot 2025-04-29 at 14 17 57

If this is intended to stay private, I can do the steps myself, but with all the release changes in preview, I wanted to follow-up in the context where change was made.

@nlopes nlopes mentioned this pull request May 10, 2025
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.

3 participants