Undocumented changes to file object
Opened this issue ยท 11 comments
Packages:
Select all that apply:
-
@slack/web-api
-
@slack/rtm-api
-
@slack/webhooks
-
@slack/oauth
-
@slack/socket-mode
-
@slack/types
- I don't know
Reproducible in:
The Slack SDK version
@slack/web-api@6.12.0
@slack/types@2.12.0
Node.js runtime version
v18.11.0
OS info
ProductName: macOS
ProductVersion: 12.5
BuildVersion: 21G72
Darwin Kernel Version 21.6.0: Sat Jun 18 17:07:22 PDT 2022; root:xnu-8020.140.41~1/RELEASE_ARM64_T6000
Steps to reproduce:
const fileUploadResp = await slackClient.files.upload({
channels: channelId,
file: readFileStream(pathToFile),
filename: file.name || 'unknown_filename',
filetype: file.filetype,
initial_comment: `Uploaded by <@${message.authorId}>`,
thread_ts: threadTs,
})
const uploadedFile = fileUploadResp.file
console.log(uploadedFile)
const fileUploadV2Resp = await slackClient.files.uploadV2({
channel_id: channelId,
file_uploads: files.map(file => ({
file: readFileStream(filePathsByFileId[file.id]),
filename: file.name || 'unknown_filename',
})),
initial_comment: `Uploaded by <@${message.authorId}>`,
thread_ts: threadTs,
})
const fileUploads = fileUploadV2Resp.files[0].files
console.log(fileUploads[0])
Expected result:
An example of a file object returned by the to-be-deprecated files.upload
method. This matches the object shown here in the docs: https://api.slack.com/types/file
{
id: 'F07P0LRQQ6N',
created: 1727277704,
timestamp: 1727277704,
name: 'Screen Shot 2024-06-04 at 4.44.34 PM.png',
title: 'Screen Shot 2024-06-04 at 4.44.34 PM',
mimetype: 'image/png',
filetype: 'png',
pretty_type: 'PNG',
user: 'U04JUMFCGE6',
user_team: 'TU7B08JCV',
editable: false,
size: 997126,
mode: 'hosted',
is_external: false,
external_type: '',
is_public: false,
public_url_shared: false,
display_as_bot: false,
username: '',
url_private: 'https://files.slack.com/files-pri/TU7B08JCV-F07P0LRQQ6N/screen_shot_2024-06-04_at_4.44.34_pm.png',
url_private_download: 'https://files.slack.com/files-pri/TU7B08JCV-F07P0LRQQ6N/download/screen_shot_2024-06-04_at_4.44.34_pm.png',
media_display_type: 'unknown',
thumb_64: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_64.png',
thumb_80: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_80.png',
thumb_360: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_360.png',
thumb_360_w: 360,
thumb_360_h: 184,
thumb_480: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_480.png',
thumb_480_w: 480,
thumb_480_h: 245,
thumb_160: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_160.png',
thumb_720: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_720.png',
thumb_720_w: 720,
thumb_720_h: 368,
thumb_800: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_800.png',
thumb_800_w: 800,
thumb_800_h: 408,
thumb_960: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_960.png',
thumb_960_w: 960,
thumb_960_h: 490,
thumb_1024: 'https://files.slack.com/files-tmb/TU7B08JCV-F07P0LRQQ6N-f6d08ddef0/screen_shot_2024-06-04_at_4.44.34_pm_1024.png',
thumb_1024_w: 1024,
thumb_1024_h: 523,
original_w: 2668,
original_h: 1362,
thumb_tiny: 'AwAYADDR79TSjnvSY5pQMUAL+NFFFABR3opO9AC4ooooAKKKKAENH8VDdqP4qAP/2Q==',
permalink: 'https://wranglesoft.slack.com/files/U04JUMFCGE6/F07P0LRQQ6N/screen_shot_2024-06-04_at_4.44.34_pm.png',
permalink_public: 'https://slack-files.com/TU7B08JCV-F07P0LRQQ6N-819dd60ac2',
comments_count: 0,
is_starred: false,
shares: { private: { D04KJCKG3G8: [Array] } },
channels: [],
groups: [],
ims: [ 'D04KJCKG3G8' ],
has_more_shares: false,
has_rich_preview: false,
file_access: 'visible'
}
Actual result:
This is an example of a file object returned by the new, recommended files.uploadV2
method. You can see that many fields are either missing or empty when compared to the file type shown in the api docs. Most notably, for our purposes, are mimetype
, shares
, channels
, and groups
. The new, reduced file object is consistent between the initial return from files.uploadV2
and subsequent calls to files.info
after a successful upload.
{
id: 'F07P0GNNNKU',
created: 1727276530,
timestamp: 1727276530,
name: 'Screen Shot 2024-06-04 at 4.44.34 PM.png',
title: 'Screen Shot 2024-06-04 at 4.44.34 PM.png',
mimetype: '',
filetype: '',
pretty_type: '',
user: 'U04JUMFCGE6',
user_team: 'TU7B08JCV',
editable: false,
size: 997126,
mode: 'hosted',
is_external: false,
external_type: '',
is_public: false,
public_url_shared: false,
display_as_bot: false,
username: '',
url_private: 'https://files.slack.com/files-pri/TU7B08JCV-F07P0GNNNKU/screen_shot_2024-06-04_at_4.44.34_pm.png',
url_private_download: 'https://files.slack.com/files-pri/TU7B08JCV-F07P0GNNNKU/download/screen_shot_2024-06-04_at_4.44.34_pm.png',
media_display_type: 'unknown',
permalink: 'https://wranglesoft.slack.com/files/U04JUMFCGE6/F07P0GNNNKU/screen_shot_2024-06-04_at_4.44.34_pm.png',
permalink_public: 'https://slack-files.com/TU7B08JCV-F07P0GNNNKU-119478101c',
favorites: [],
is_starred: false,
shares: {},
channels: [],
groups: [],
ims: [],
has_more_shares: false,
has_rich_preview: false,
file_access: 'visible',
comments_count: 0
}
Requirements
For general questions/issues about Slack API platform or its server-side, could you submit questions at https://my.slack.com/help/requests/new instead. ๐
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
Silly question: was the file shared to any channel or group during upload? By default, if sharing options are not specified, then the file remains 'private', that is, accessible only by the user or app that uploaded it. If this is the case, I would expect the shares
, groups
and channels
fields to remain empty.
Also, some fields like mimetype
may not be populated immediately; uploaded files go through a malware scanning process, which puts the file into a worker queue. Only after this scanning process is complete do file contents get fully 'parsed' by Slack into fields like e.g. mimetype
. So, also depending on the timing (that is, the completed-or-not state of the malware scan), the responses from the files.info
API may differ.
The fileUploadV2
method is a combination of three actions:
- Call the
getUploadURLExternal
API - HTTP POST the file contents to the URL returned in the above API call
- Call the
completeUploadURLExternal
API
Thanks for the quick reply. In both cases (using files.upload
and files.uploadV2
) the file is being uploaded to a direct message thread. Beyond the channel_id
and thread_ts
I am not sure what you mean by "sharing options". Is there a way to know the status of the malware scanning process? Calling files.info
on the file uploaded using the files.uploadV2
method simply return ok: true
and the file as shown above (missing mimetype, etc...)
It seems like you are providing a channelId
in the upload call, but looks like this is a DM... hmm.
What token is being used for the upload API call? A user token? A bot token? I'm trying to understand the baseline permissions to this file based on which token is used to upload it. Is the bot uploading the file as itself, or is your app using a user token and uploading the file on behalf of a user?
A bot token. I can also give a reproducible example that uploads to a public or private channel if that would be helpful. I guess my main point is that response has changed between upload
and uploadV2
when uploading to the same thread and channel.
Not sure I follow what you mean by 'response has changed.' If I understand correctly, you previously used the upload
API, and are now moving to the new method since the old upload
API is being deprecated, correct? If so, these are different APIs (i.e. files.upload
vs. the combination of APIs I mentioned above), with different request and response schemas.
A reproduction sample is always helpful! Much easier to discuss with specifics in hand rather than in abstract.
Hmm. Possibly a misunderstanding on my part. Based on posts like this one I had the impression that the new upload steps:
Call the getUploadURLExternal API
HTTP POST the file contents to the URL returned in the above API call
Call the completeUploadURLExternal API
were intended to be a replacement for the old upload
API.
I do see that the File
interface for FilesUploadResponse
does differ from the File
interface for FilesCompleteUploadExternalResponse
, but I would expect that the fields they share in common, e.g. mimetype
, ims
, groups
, shares
etc ... should have the same values for the same file. If that is not the case then I think that should be documented somewhere.
I agree. We could probably spend some time validating the response types and making this clearer in our documentation. I'll leave this issue open as an enhancement to tackle.
Thank you for you help! I have now tried manually polling files.info
after completing the upload using completeUploadExternal
and found that mimetype
was eventually populated. It would be helpful to have some indication in the files.info
response that the upload job has finished rather that indirectly checking the job status by waiting for certain fields to have values. For example, the ims
field was still empty when mimetype
got a value. I would expect it to (eventually) have the channel id of the direct message, but it is not obvious when polling should stop if values become available in an unknown number of stages.
First call to files.info
{
"ok": true,
"file": {
"id": "F07P25P8BMZ",
"created": 1727292892,
"timestamp": 1727292892,
"name": "Screen Shot 2024-08-05 at 11.26.42 AM.png",
"title": "Screen Shot 2024-08-05 at 11.26.42 AM.png",
"mimetype": "",
"filetype": "",
"pretty_type": "",
"user": "U04JUMFCGE6",
"user_team": "TU7B08JCV",
"editable": false,
"size": 125862,
"mode": "hosted",
"is_external": false,
"external_type": "",
"is_public": false,
"public_url_shared": false,
"display_as_bot": false,
"username": "",
"url_private": "https://files.slack.com/files-pri/TU7B08JCV-F07P25P8BMZ/screen_shot_2024-08-05_at_11.26.42_am.png",
"url_private_download": "https://files.slack.com/files-pri/TU7B08JCV-F07P25P8BMZ/download/screen_shot_2024-08-05_at_11.26.42_am.png",
"media_display_type": "unknown",
"permalink": "https://wranglesoft.slack.com/files/U04JUMFCGE6/F07P25P8BMZ/screen_shot_2024-08-05_at_11.26.42_am.png",
"permalink_public": "https://slack-files.com/TU7B08JCV-F07P25P8BMZ-a66148e629",
"favorites": [],
"is_starred": false,
"shares": {},
"channels": [],
"groups": [],
"ims": [],
"has_more_shares": false,
"has_rich_preview": false,
"file_access": "visible",
"comments_count": 0
},
"comments": [],
"response_metadata": {
"next_cursor": "",
"scopes": [
"chat:write",
"chat:write.public",
"commands",
"im:history",
"users:read",
"users.profile:read",
"workflow.steps:execute",
"channels:history",
"channels:join",
"usergroups:read",
"channels:read",
"groups:history",
"groups:read",
"chat:write.customize",
"im:read",
"groups:write",
"reactions:read",
"team:read",
"reactions:write",
"files:write",
"files:read"
],
"acceptedScopes": [
"files:read"
]
}
}
Second call to files.info
{
"ok": true,
"file": {
"id": "F07P25P8BMZ",
"created": 1727292892,
"timestamp": 1727292892,
"name": "Screen Shot 2024-08-05 at 11.26.42 AM.png",
"title": "Screen Shot 2024-08-05 at 11.26.42 AM.png",
"mimetype": "image/png",
"filetype": "png",
"pretty_type": "PNG",
"user": "U04JUMFCGE6",
"user_team": "TU7B08JCV",
"editable": false,
"size": 125862,
"mode": "hosted",
"is_external": false,
"external_type": "",
"is_public": false,
"public_url_shared": false,
"display_as_bot": false,
"username": "",
"url_private": "https://files.slack.com/files-pri/TU7B08JCV-F07P25P8BMZ/screen_shot_2024-08-05_at_11.26.42_am.png",
"url_private_download": "https://files.slack.com/files-pri/TU7B08JCV-F07P25P8BMZ/download/screen_shot_2024-08-05_at_11.26.42_am.png",
"media_display_type": "unknown",
"thumb_64": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_64.png",
"thumb_80": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_80.png",
"thumb_360": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_360.png",
"thumb_360_w": 360,
"thumb_360_h": 105,
"thumb_480": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_480.png",
"thumb_480_w": 480,
"thumb_480_h": 140,
"thumb_160": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_160.png",
"thumb_720": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_720.png",
"thumb_720_w": 720,
"thumb_720_h": 211,
"thumb_800": "https://files.slack.com/files-tmb/TU7B08JCV-F07P25P8BMZ-16f89dab26/screen_shot_2024-08-05_at_11.26.42_am_800.png",
"thumb_800_w": 800,
"thumb_800_h": 234,
"original_w": 848,
"original_h": 248,
"thumb_tiny": "AwAOADC8zFQvzKv1ppkP9+OnkEqMNj8M0mx/74/75oATec43pSeYeu5MfjT9r/3x/wB80m1/74/75oAVSxIOVK+op460za/9/wDSnKCOpz+FAH//2Q==",
"permalink": "https://wranglesoft.slack.com/files/U04JUMFCGE6/F07P25P8BMZ/screen_shot_2024-08-05_at_11.26.42_am.png",
"permalink_public": "https://slack-files.com/TU7B08JCV-F07P25P8BMZ-a66148e629",
"favorites": [],
"is_starred": false,
"shares": {},
"channels": [],
"groups": [],
"ims": [],
"has_more_shares": false,
"has_rich_preview": false,
"file_access": "visible",
"comments_count": 0
},
"comments": [],
"response_metadata": {
"next_cursor": "",
"scopes": [
"chat:write",
"chat:write.public",
"commands",
"im:history",
"users:read",
"users.profile:read",
"workflow.steps:execute",
"channels:history",
"channels:join",
"usergroups:read",
"channels:read",
"groups:history",
"groups:read",
"chat:write.customize",
"im:read",
"groups:write",
"reactions:read",
"team:read",
"reactions:write",
"files:write",
"files:read"
],
"acceptedScopes": [
"files:read"
]
}
}
What about listening on various file_*
events to get an idea of when the file has completed processing? file_created
and file_shared
could work, depending on your use case.
Thank you, I will give that a try. Even does work it would be a much more a significant change to the way our application handles file uploads.
Given that this article suggesting polling files.info
as an approach I think that method should give a clearer indication of when the upload job has finished.
Fair! I will throw that recommendation the Files' team's way, but I also suggest you submit that as a feature request / API extension via the feedback@slack.com email; generally speaking, Slack leadership takes customer reports more seriously with respect to prioritizing work than my suggestions ๐