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

feat: Slack Avatar integration #27849

Merged
merged 3 commits into from
Apr 16, 2024
Merged

feat: Slack Avatar integration #27849

merged 3 commits into from
Apr 16, 2024

Conversation

mistercrunch
Copy link
Member

@mistercrunch mistercrunch commented Apr 2, 2024

SUMMARY

Our current Avatars are a little dry, using a limited variety of colors and showing the user's initials. The app could use a little more personality. Now given we already have a slack integration and administrators can configure their SLACK_API_TOKEN, I was thinking we can use this integration to get Slack's avatars integrated in Superset. Given the popularity of Slack and users familiarity with the avatars there, I thought it'd be neat to bring a bit more color in Superset.

Note that I'm also introducing the config SLACK_ENABLE_AVATARS which is False by default, and added a note to UPDATING.md for release managers to switch it on if desired.

The approach here is centered around a new endpoint /api/v1/user/{id}/avatar.png, which fetches from a new column in
UserAttribute (our one-to-one extension to FAB's User model) name avatar_url, and redirects (301) to the destination. When loading for the first time, if information is empty and Slack is configured, we call Slack for the avatar URL, which has a secret location, but serves the image from outside the authentication part of their API once known. If the feature is off or no avatar url is set for the user, we simply return a 204 (no-content), preventing from logging errors or anything negative as this is expected.

The avatar_url field could be used programmatically in environments to serve other urls, but at this time I did not implement a GUI for users to self-set their avatar for instance. I think in most cases people will want to integrate with some external service (MSFT Team, Discord, ...). The approach I took here could be expanded to work with Gravatar for instance.

About Perf

This approach while unconventional (is it?) should perform very well. Say in an environment with 1000 users, the Slack API should trigger once-ish for each user in the database to fill the avatar_url for that user (1000 api hits). For each user navigating the website, a single hit to /api/v1/users/{some_user_id}/avatar.png where the database is looked up is sufficient (up to 1M hits, but super low-key endpoint), after which the permanent redirect is cached client-side. The 204 should get cached like any other request following the configured cache-control in place.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Screenshot 2024-04-01 at 10 07 03 PM

@github-actions github-actions bot added risk:db-migration PRs that require a DB migration api Related to the REST API labels Apr 2, 2024
.gitignore Outdated Show resolved Hide resolved
Copy link

codecov bot commented Apr 2, 2024

Codecov Report

Attention: Patch coverage is 56.86275% with 22 lines in your changes are missing coverage. Please review.

Project coverage is 59.98%. Comparing base (0d0e47a) to head (63aacce).

Files Patch % Lines
superset/views/users/api.py 36.36% 21 Missing ⚠️
superset/utils/slack.py 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #27849      +/-   ##
==========================================
- Coverage   69.83%   59.98%   -9.85%     
==========================================
  Files        1920     1921       +1     
  Lines       75242    75284      +42     
  Branches     8423     8423              
==========================================
- Hits        52546    45160    -7386     
- Misses      20635    28062    +7427     
- Partials     2061     2062       +1     
Flag Coverage Δ
hive 48.92% <44.68%> (-0.01%) ⬇️
javascript 57.40% <100.00%> (-0.02%) ⬇️
mysql ?
postgres ?
presto 53.62% <44.68%> (-0.02%) ⬇️
python 62.75% <53.19%> (-20.41%) ⬇️
sqlite ?
unit 56.76% <53.19%> (-0.02%) ⬇️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@github-actions github-actions bot added the github_actions Pull requests that update GitHub Actions code label Apr 2, 2024
@github-actions github-actions bot removed the github_actions Pull requests that update GitHub Actions code label Apr 5, 2024
@mistercrunch mistercrunch force-pushed the avatar_url branch 2 times, most recently from 77c5a6a to 1162ff7 Compare April 11, 2024 06:18
@mistercrunch mistercrunch marked this pull request as ready for review April 11, 2024 16:03
@mistercrunch mistercrunch requested a review from a team as a code owner April 11, 2024 16:03
superset/config.py Outdated Show resolved Hide resolved
superset/config.py Outdated Show resolved Hide resolved
superset/utils/slack.py Outdated Show resolved Hide resolved
superset/models/user_attributes.py Outdated Show resolved Hide resolved
superset/views/users/api.py Outdated Show resolved Hide resolved
superset/views/users/api.py Outdated Show resolved Hide resolved
)
user_attrs.avatar_url = avatar_url
db.session.add(user_attrs)
db.session.commit()
Copy link
Member

Choose a reason for hiding this comment

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

by the book, a GET should not change any state. What about creating a celery beat job that would collect all the existing user avatars?

Copy link
Member Author

@mistercrunch mistercrunch Apr 16, 2024

Choose a reason for hiding this comment

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

This is the only element I did not fully address because it's effectively the feature here. Note that mutations will be very very sparse here. Mutation only happen if:

  • feature is on (SLACK_ENABLE_AVATAR + SLACK_API_TOKEN)
  • it's the very first time the avatar is requested for a given target user in the whole environment, doing a tiny update of UserAttributes.avatar_url once with a normal-sized string

In all normal day-to-day

  • upon a new browser requesting an avatar, the GET does as a normal get (a one cell database lookup)
  • 301 gets cached
  • target image gets cached

superset/views/users/api.py Show resolved Hide resolved
tests/integration_tests/users/api_tests.py Outdated Show resolved Hide resolved
superset/views/users/api.py Outdated Show resolved Hide resolved
Our current Avatars are a little dry, using a limited variety of colors
and showing the user's initials. The app could use a little more
personality.

Now given we already have a slack integration and administrators can
configure their `SLACK_API_TOKEN`, I was thinking we can use this
integration to get Slack's avatars integrated in Superset. Given the
popularity of Slack and users familiarity with the avatars there,
I thought it'd be neat to bring a bit more color in Superset.

The approach here is centered around a new endpoint
`/api/v1/user/{id}/avatar.png`, which fetches from a new column in
UserAttribute (our one-to-one extension to FAB's User model) for
avatar_url, and redirects to the destination. When loading for the
first time, if information is empty and Slack is configured, we ask
Slack for the URL, which is secret, but serves the image from outside
the authentication part of their API.
@mistercrunch
Copy link
Member Author

@dpgaspar I addressed pretty much all feedback. Personally prefer avoiding the CELERY_BEAT configuration, but open to consider that approach too if we decide it's preferable.

@mistercrunch mistercrunch merged commit e9c0ca5 into master Apr 16, 2024
34 checks passed
@mistercrunch mistercrunch deleted the avatar_url branch April 16, 2024 15:40
# under the License.
"""empty message

Revision ID: bbf146925528
Copy link
Member

Choose a reason for hiding this comment

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

What does this migration do? See that upgrade and downgrade are just pass

# under the License.
"""empty message

Revision ID: 0dc386701747
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, what would this migration do?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hey, sorry about the confusion, but in alembic the migration "chain" can split and you end up with multiple heads, typically when you rebase and there's been some other migration. This migration merges the 2 heads. In theory it's possible to do the equivalent of a rebase, but that requires a bit of work. In this case I "git rebased" twice during the lifecycle of this PR and had to merge the heads 2 times, leaving this 2 empty-ish migrations.

Copy link
Member Author

Choose a reason for hiding this comment

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

qleroy pushed a commit to qleroy/superset that referenced this pull request Apr 28, 2024
jzhao62 pushed a commit to jzhao62/superset that referenced this pull request May 16, 2024
@padbk
Copy link
Contributor

padbk commented Aug 29, 2024

I've tried enabling this in 4.1.0rc2, but I get the same behaviour as this issue #28833 I think it is caused by the combination of websockets with this feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Related to the REST API preset-io risk:db-migration PRs that require a DB migration size/L
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants