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

Undocumented changes to file object #2040

Open
2 of 7 tasks
jgkingston opened this issue Sep 25, 2024 · 11 comments
Open
2 of 7 tasks

Undocumented changes to file object #2040

jgkingston opened this issue Sep 25, 2024 · 11 comments
Labels
area:typescript issues that specifically impact using the package from typescript projects auto-triage-skip docs M-T: Documentation work only enhancement M-T: A feature request for new functionality pkg:types applies to `@slack/types` semver:patch

Comments

@jgkingston
Copy link

jgkingston commented Sep 25, 2024

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

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.

@filmaj filmaj added needs info An issue that is claimed to be a bug and hasn't been reproduced, or otherwise needs more info and removed untriaged labels Sep 25, 2024
@filmaj
Copy link
Contributor

filmaj commented Sep 25, 2024

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:

@jgkingston
Copy link
Author

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...)

@filmaj
Copy link
Contributor

filmaj commented Sep 25, 2024

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?

@jgkingston
Copy link
Author

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.

@filmaj
Copy link
Contributor

filmaj commented Sep 25, 2024

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.

@jgkingston
Copy link
Author

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.

@filmaj filmaj added docs M-T: Documentation work only semver:patch enhancement M-T: A feature request for new functionality area:typescript issues that specifically impact using the package from typescript projects pkg:types applies to `@slack/types` and removed needs info An issue that is claimed to be a bug and hasn't been reproduced, or otherwise needs more info labels Sep 25, 2024
@filmaj filmaj added this to the [email protected] milestone Sep 25, 2024
@filmaj
Copy link
Contributor

filmaj commented Sep 25, 2024

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.

@jgkingston
Copy link
Author

jgkingston commented Sep 25, 2024

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"
    ]
  }
}

@filmaj
Copy link
Contributor

filmaj commented Sep 25, 2024

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.

@jgkingston
Copy link
Author

jgkingston commented Sep 25, 2024

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.

@filmaj
Copy link
Contributor

filmaj commented Sep 25, 2024

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 [email protected] email; generally speaking, Slack leadership takes customer reports more seriously with respect to prioritizing work than my suggestions 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:typescript issues that specifically impact using the package from typescript projects auto-triage-skip docs M-T: Documentation work only enhancement M-T: A feature request for new functionality pkg:types applies to `@slack/types` semver:patch
Projects
None yet
Development

No branches or pull requests

2 participants