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

chore(slack): introduce SlackClient using slack_sdk #72017

Merged
merged 9 commits into from
Jun 5, 2024

Conversation

cathteng
Copy link
Member

@cathteng cathteng commented Jun 4, 2024

Added slack_sdk here #71881

This PR adds a SlackClient that subclasses WebClient from the official Slack SDK. To make the usage the same as the in-house SlackClient, the abstraction accepts integration_id and finds the access token before initializing an instance of the WebClient.

We also want to track the response outcomes for every outgoing API call to Slack, so I added a wrapper that executes right after every single API call to increment a metric.

@cathteng cathteng requested a review from a team as a code owner June 4, 2024 16:22
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jun 4, 2024
integration = Integration.objects.filter(id=integration_id).first()

if integration is None:
raise ValueError(f"Integration with id {integration_id} not found")
Copy link
Member Author

Choose a reason for hiding this comment

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

we don't raise any errors if initialization fails in the existing SlackClient. all subsequent calls are no-ops, but i think we should fail loudly so we're aware something is broken

Copy link
Member

Choose a reason for hiding this comment

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

+1


with pytest.raises(SlackApiError):
# error raised because it's actually trying to POST
client.chat_postMessage(channel="#announcements", text="hello")
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 having difficulty mocking responses with the Slack SDK client. it seems like we might have to do something like this slackapi/python-slack-sdk#1188 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

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

ok i got something working here, please yell loudly if this is not the way to do it

Copy link

codecov bot commented Jun 4, 2024

Codecov Report

Attention: Patch coverage is 88.00000% with 12 lines in your changes missing coverage. Please review.

Project coverage is 77.93%. Comparing base (0e923cc) to head (b076f3e).
Report is 19 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master   #72017       +/-   ##
===========================================
+ Coverage   56.86%   77.93%   +21.06%     
===========================================
  Files        6561     6572       +11     
  Lines      292452   292863      +411     
  Branches    50480    50550       +70     
===========================================
+ Hits       166309   228243    +61934     
+ Misses     121415    58369    -63046     
- Partials     4728     6251     +1523     
Files Coverage Δ
src/sentry/integrations/slack/utils/options.py 100.00% <100.00%> (ø)
src/sentry/options/defaults.py 100.00% <100.00%> (ø)
src/sentry/integrations/slack/sdk_client.py 92.85% <92.85%> (ø)
.../sentry/integrations/slack/actions/notification.py 88.15% <80.00%> (+54.82%) ⬆️

... and 1971 files with indirect coverage changes

Copy link
Member

@sentaur-athena sentaur-athena left a comment

Choose a reason for hiding this comment

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

I understand some of my comments are for existing code so I hope they're not too annoying 😅

if payload_blocks := blocks.get("blocks"):
payload["blocks"] = orjson.dumps(payload_blocks).decode()
json_blocks = orjson.dumps(payload_blocks).decode()
Copy link
Member

Choose a reason for hiding this comment

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

Is there a test covering this line of code? After #inc-779 I get scared whenever I see orjson.dumps

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 added testing to this area of the code in #72066, it should be covered now

# temporarily log the payload so we can debug message failures
log_params["payload"] = orjson.dumps(payload).decode()

self.logger.info(
Copy link
Member

Choose a reason for hiding this comment

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

Why is this just info vs error? Also don't we want this in sentry?

"channel_name": self.get_option("channel"),
}
# temporarily log the payload so we can debug message failures
log_params["payload"] = orjson.dumps(payload).decode()
Copy link
Member

Choose a reason for hiding this comment

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

orjson warning! 😅 please make sure there's test coverage

Copy link
Member Author

Choose a reason for hiding this comment

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

added a test that goes through this flow no problem :)

src/sentry/integrations/slack/sdk_client.py Outdated Show resolved Hide resolved
integration = Integration.objects.filter(id=integration_id).first()

if integration is None:
raise ValueError(f"Integration with id {integration_id} not found")
Copy link
Member

Choose a reason for hiding this comment

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

+1

Copy link
Contributor

@ykamo001 ykamo001 left a comment

Choose a reason for hiding this comment

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

Thanks for taking on this work and listening to the feedback/recommendations!!

Comment on lines +29 to +34
extra = {
"integration": "slack",
"status_string": str(code),
"error": error,
}
logger.info("integration.http_response", extra=extra)
Copy link
Contributor

Choose a reason for hiding this comment

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

are we going to know which method failed though? Like which API call? Any chance we can capture that as well to help us understand which method is failing?

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 think i can pass this in the logger if i pass the method in

Copy link
Member Author

@cathteng cathteng Jun 5, 2024

Choose a reason for hiding this comment

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

like method.__name__

Copy link
Member Author

Choose a reason for hiding this comment

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

let me do this in a followup, and move the metaclass stuff to integrations.base. currently the only API call is chat.postMessage

@cathteng cathteng merged commit 00eb14e into master Jun 5, 2024
49 of 50 checks passed
@cathteng cathteng deleted the cathy/simple-slack-sdk-client branch June 5, 2024 19:56
Copy link

sentry-io bot commented Jun 6, 2024

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

  • ‼️ RpcRemoteException: integration.get_integration: RPC failed, max retries reached. sentry.tasks.post_process.post_process_group View Issue

Did you find this useful? React with a 👍 or 👎

@github-actions github-actions bot locked and limited conversation to collaborators Jun 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Scope: Backend Automatically applied to PRs that change backend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants