Skip to content

feat(web-api): add configs to toggle absolute url usage in dynamic api calls #2176

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

Merged
merged 7 commits into from
Mar 19, 2025

Conversation

zimeg
Copy link
Member

@zimeg zimeg commented Mar 13, 2025

Summary

This PR adds a new allowAbsoluteUrls option to the WebClient constructor that can toggle if the slackApiUrl is always the prefix of the request URL when calling Slack API methods or if an absolute URL can override this.

To keep the current behavior, allowAbsoluteUrls defaults to true 📚

Preview

The following code attempts to make a request to "https://slack.com/api/https://example.com/method"!

const client = new webapi.WebClient(process.env.SLACK_TOKEN, {
  allowAbsoluteUrls: false,
});

const _response = await client.apiCall("https://example.com/method", { /* ... */ })
[DEBUG]  web-api:WebClient:0 initialized
[DEBUG]  web-api:WebClient:0 apiCall('https://example.com/method') start
[DEBUG]  web-api:WebClient:0 http request url: https://slack.com/api/https://example.com/method

Reviewers

The above example can be used with Slack API methods in "dot" notation - chat.postMessage - and different values of the allowAbsoluteUrls option 👾

For fast reference, it might be neat to use the absolute "https://slack.com/api/chat.postMessage" URL.

Requirements

@zimeg zimeg added semver:minor enhancement M-T: A feature request for new functionality pkg:web-api applies to `@slack/web-api` labels Mar 13, 2025
@zimeg zimeg added this to the [email protected] milestone Mar 13, 2025
@zimeg zimeg self-assigned this Mar 13, 2025
Copy link

codecov bot commented Mar 13, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 91.99%. Comparing base (e1e3df0) to head (b154b6b).
Report is 1 commits behind head on main.

✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2176      +/-   ##
==========================================
+ Coverage   91.94%   91.99%   +0.05%     
==========================================
  Files          38       38              
  Lines       10328    10386      +58     
  Branches      652      657       +5     
==========================================
+ Hits         9496     9555      +59     
+ Misses        820      819       -1     
  Partials       12       12              
Flag Coverage Δ
cli-hooks 95.23% <ø> (ø)
cli-test 94.76% <ø> (ø)
oauth 77.39% <ø> (ø)
socket-mode 61.82% <ø> (ø)
web-api 96.93% <100.00%> (+0.04%) ⬆️
webhook 96.65% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zimeg zimeg requested a review from technically-tracy March 13, 2025 03:41
Copy link
Member Author

@zimeg zimeg left a comment

Choose a reason for hiding this comment

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

📝 A few quick notes on these changes for the kind reviewers!

Comment on lines +135 to +146
A dynamic method name can either be in the format shown above, to be sent with the [`slackApiUrl`](#custom-api-url)
option, or as an absolute URL. Setting the
[`allowAbsoluteUrls`](/reference/web-api/interfaces/WebClientOptions#allowabsoluteurls)
option to `false` sends all requests to the `slackApiUrl` option value:

```javascript
const { WebClient } = require('@slack/web-api');

const web = new WebClient(token, {
allowAbsoluteUrls: false,
});
```
Copy link
Member Author

Choose a reason for hiding this comment

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

Open to suggestions as always, but wanted to make sure a note of this option is included in these details 📚 ✨

docs

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 649 to 651
const requestURL = url.startsWith('https' || 'http') && this.allowAbsoluteUrls
? url
: `${this.axios.getUri() + url}`;
Copy link
Member Author

Choose a reason for hiding this comment

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

📝 The requestURL variable is only used in debug logs, axios details configured separately, though this did seem close to what the logic of allowAbsoluteUrls might do!

Copy link
Contributor

@WilliamBergamin WilliamBergamin left a comment

Choose a reason for hiding this comment

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

These changes look good 💯

I'm just not clear on when a developer would want to do this, why would they be passing and override URL if it will have not effect 🤔 instead of this addition should we advise them to not pass the override value if they don't want to override the URL?

@@ -618,7 +646,8 @@ export class WebClient extends Methods {
// TODO: better input types - remove any
const task = () =>
this.requestQueue.add(async () => {
const requestURL = url.startsWith('https' || 'http') ? url : `${this.axios.getUri() + url}`;
const requestURL =
url.startsWith('https' || 'http') && this.allowAbsoluteUrls ? url : `${this.axios.getUri() + url}`;
Copy link
Contributor

@WilliamBergamin WilliamBergamin Mar 13, 2025

Choose a reason for hiding this comment

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

The complexity of this line seems to be growing, could it be more readable if we broke it up in an if statement 🤔 ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I was thinking the same... So much is happening here 😓

I'll explore the if statements, but do also like the const allowed in the current setup 👾

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe breaking the logic out into its own function could make sense

private deriveRequestUrl(url: string): string {
    if (this.allowAbsoluteUrls) {
        return url.startsWith('https' || 'http') ? url : `${this.axios.getUri() + url}`
    }
    return `${this.axios.getUri() + url}`;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Similar logic was moved into a deriveRequestUrl function in 5f98b56! I forget private functions can be useful sometimes - it's a great call 😅

@zimeg
Copy link
Member Author

zimeg commented Mar 13, 2025

@WilliamBergamin Thanks for checking out the changes! ✨

I don't believe this option is needed for scripts or apps making known API calls, but this option can be useful in setups that might pass unknown methods to WebClient.apiCall

The same guards against absolute URLs could definitely be added to application code, prefixing all .apiCall requests with the default API URL as you suggest, but IMO this implementation might be forgotten in some places which makes me think having an SDK implementation is preferred 👁️‍🗨️

Copy link
Contributor

@WilliamBergamin WilliamBergamin left a comment

Choose a reason for hiding this comment

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

I see how the changes could be useful for security use cases 🤔

I've added one more comment about breaking the requestUrl logic into a method to increase readability, let me know what you think

@zimeg zimeg requested a review from WilliamBergamin March 14, 2025 21:22
@zimeg
Copy link
Member Author

zimeg commented Mar 14, 2025

@WilliamBergamin 🙏 Thanks for suggesting the improvement to code structures. I'm hoping this'll make later changes around this logic a bit quicker to understand!

Tests continue to pass with that change too, but please let me know if other changes are requested!

Copy link
Contributor

@WilliamBergamin WilliamBergamin left a comment

Choose a reason for hiding this comment

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

Nice 💯 this looks good

@zimeg
Copy link
Member Author

zimeg commented Mar 19, 2025

@WilliamBergamin Thanks so much calling out the changes of this PR 🙏

I did a bit more testing and found one thing strange with http URLs fixed b154b6b. AFAICT this change only updates the request URL shown in logs since axios handles actual request logic, which is why the tests were passing 📺 ✨

Now is seeming like an alright time to merge this! I'll open a release PR for @slack/[email protected] but wait until tomorrow to release and make adjacent updates if this is all alright. Please let me know if other changes are needed before that though! 🔍

@zimeg zimeg merged commit 6d62c40 into main Mar 19, 2025
59 checks passed
@zimeg zimeg deleted the feat-web-api-allow-absolute-urls-option branch March 19, 2025 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement M-T: A feature request for new functionality pkg:web-api applies to `@slack/web-api` semver:minor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants