From bb98d48747df961310995e86af6fa174dd09190b Mon Sep 17 00:00:00 2001 From: David Whittaker Date: Tue, 12 Nov 2024 19:59:26 -0800 Subject: [PATCH] ignoring client for Slack calls to make subsequent calls more efficient --- src/dispatch/incident/scheduled.py | 3 +- src/dispatch/plugins/dispatch_slack/plugin.py | 3 +- .../plugins/dispatch_slack/service.py | 68 ++++++++++++++++--- 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/dispatch/incident/scheduled.py b/src/dispatch/incident/scheduled.py index a30b0cbf236d..301bd1448189 100644 --- a/src/dispatch/incident/scheduled.py +++ b/src/dispatch/incident/scheduled.py @@ -354,7 +354,8 @@ def incident_report_weekly(db_session: Session, project: Project): @timer @scheduled_project_task def incident_sync_members(db_session: Session, project: Project): - """Checks the members of all conversations and ensures they are in the incident.""" + """Checks the members of all conversations associated with active + and stable incidents and ensures they are in the incident.""" plugin = plugin_service.get_active_instance( db_session=db_session, project_id=project.id, diff --git a/src/dispatch/plugins/dispatch_slack/plugin.py b/src/dispatch/plugins/dispatch_slack/plugin.py index de36b2177ecf..db9b02936156 100644 --- a/src/dispatch/plugins/dispatch_slack/plugin.py +++ b/src/dispatch/plugins/dispatch_slack/plugin.py @@ -53,6 +53,7 @@ does_user_exist, emails_to_user_ids, get_user_avatar_url, + get_user_info_by_id, get_user_profile_by_email, is_user, rename_conversation, @@ -467,7 +468,7 @@ def get_all_member_emails(self, conversation_id: str) -> list[str]: member_emails = [] for member_id in member_ids: if is_user(config=self.configuration, user_id=member_id): - user = client.users_info(user=member_id).get("user") + user = get_user_info_by_id(client, member_id) if user: member_emails.append(user["profile"]["email"]) diff --git a/src/dispatch/plugins/dispatch_slack/service.py b/src/dispatch/plugins/dispatch_slack/service.py index 433023af5d84..69557994e4f8 100644 --- a/src/dispatch/plugins/dispatch_slack/service.py +++ b/src/dispatch/plugins/dispatch_slack/service.py @@ -25,6 +25,23 @@ log = logging.getLogger(__name__) +class WebClientWrapper: + """A wrapper for WebClient to make all instances with same token equal for caching.""" + + def __init__(self, client): + self._client = client + + @property + def client(self): + return self._client + + def __eq__(self, other): + return other._client.token == self._client.token + + def __hash__(self): + return hash(type(self._client.token)) + + def create_slack_client(config: SlackConversationConfiguration) -> WebClient: """Creates a Slack Web API client.""" return WebClient(token=config.api_bot_token.get_secret_value()) @@ -179,28 +196,44 @@ def list_conversation_messages(client: WebClient, conversation_id: str, **kwargs @functools.lru_cache() +def _get_domain(wrapper: WebClientWrapper) -> str: + """Gets the team's Slack domain.""" + return make_call(wrapper.client, SlackAPIGetEndpoints.team_info)["team"]["domain"] + + def get_domain(client: WebClient) -> str: """Gets the team's Slack domain.""" - return make_call(client, SlackAPIGetEndpoints.team_info)["team"]["domain"] + return _get_domain(WebClientWrapper(client)) @functools.lru_cache() +def _get_user_info_by_id(wrapper: WebClientWrapper, user_id: str) -> dict: + return make_call(wrapper.client, SlackAPIGetEndpoints.users_info, user=user_id)["user"] + + def get_user_info_by_id(client: WebClient, user_id: str) -> dict: """Gets profile information about a user by id.""" - return make_call(client, SlackAPIGetEndpoints.users_info, user=user_id)["user"] + return _get_user_info_by_id(WebClientWrapper(client), user_id) @functools.lru_cache() +def _get_user_info_by_email(wrapper: WebClientWrapper, email: str) -> dict: + """Gets profile information about a user by email.""" + return make_call(wrapper.client, SlackAPIGetEndpoints.users_lookup_by_email, email=email)[ + "user" + ] + + def get_user_info_by_email(client: WebClient, email: str) -> dict: """Gets profile information about a user by email.""" - return make_call(client, SlackAPIGetEndpoints.users_lookup_by_email, email=email)["user"] + return _get_user_info_by_email(WebClientWrapper(client), email) @functools.lru_cache() -def does_user_exist(client: WebClient, email: str) -> bool: +def _does_user_exist(wrapper: WebClientWrapper, email: str) -> bool: """Checks if a user exists in the Slack workspace by their email.""" try: - get_user_info_by_email(client, email) + get_user_info_by_email(wrapper.client, email) return True except SlackApiError as e: if e.response["error"] == SlackAPIErrorCode.USERS_NOT_FOUND: @@ -209,21 +242,38 @@ def does_user_exist(client: WebClient, email: str) -> bool: raise +def does_user_exist(client: WebClient, email: str) -> bool: + """Checks if a user exists in the Slack workspace by their email.""" + return _does_user_exist(WebClientWrapper(client), email) + + @functools.lru_cache() +def _get_user_profile_by_id(wrapper: WebClientWrapper, user_id: str) -> dict: + """Gets profile information about a user by id.""" + return make_call(wrapper.client, SlackAPIGetEndpoints.users_profile_get, user_id=user_id)[ + "profile" + ] + + def get_user_profile_by_id(client: WebClient, user_id: str) -> dict: """Gets profile information about a user by id.""" - return make_call(client, SlackAPIGetEndpoints.users_profile_get, user_id=user_id)["profile"] + return _get_user_profile_by_id(WebClientWrapper(client), user_id) @functools.lru_cache() -def get_user_profile_by_email(client: WebClient, email: str) -> SlackResponse: +def _get_user_profile_by_email(wrapper: WebClientWrapper, email: str) -> SlackResponse: """Gets extended profile information about a user by email.""" - user = get_user_info_by_email(client, email) - profile = get_user_profile_by_id(client, user["id"]) + user = get_user_info_by_email(wrapper.client, email) + profile = get_user_profile_by_id(wrapper.client, user["id"]) profile["tz"] = user["tz"] return profile +def get_user_profile_by_email(client: WebClient, email: str) -> SlackResponse: + """Gets extended profile information about a user by email.""" + return _get_user_profile_by_email(WebClientWrapper(client), email) + + def get_user_email(client: WebClient, user_id: str) -> str: """Gets the user's email.""" user_info = get_user_info_by_id(client, user_id)